From b3f7c8d91b6bc762bd2360c5e5d5cc91706c2c56 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Mon, 18 May 2026 14:58:11 +0200 Subject: [PATCH 01/54] bind circuits of proof agg to vk hashes --- .../test/BfvDecryptionVerifier.spec.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts index c8e8006ee..09a4cde05 100644 --- a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts @@ -230,6 +230,66 @@ describe("BfvDecryptionVerifier", function () { expect(result).to.equal(false); }); + it("returns false when c6_fold key hash does not match", async function () { + const revertingVerifier = await ( + await ethers.getContractFactory("RevertOnVerifyCircuitVerifier") + ).deploy(); + await revertingVerifier.waitForDeployment(); + + const bfvDecryptionVerifier = await ( + await ethers.getContractFactory("BfvDecryptionVerifier") + ).deploy( + await revertingVerifier.getAddress(), + EXPECTED_C6_FOLD_KEY_HASH, + EXPECTED_C7_KEY_HASH, + ); + await bfvDecryptionVerifier.waitForDeployment(); + + const messageCoeffs = [1n, 2n, 3n]; + const publicInputs = buildPublicInputsWithMessage(messageCoeffs, 402, [ + ethers.id("wrong-c6"), + EXPECTED_C7_KEY_HASH, + ]); + const plaintextHash = plaintextToHash(messageCoeffs); + const proof = encodeProof("0x01", publicInputs); + + const result = await bfvDecryptionVerifier.verify.staticCall( + plaintextHash, + proof, + ); + expect(result).to.equal(false); + }); + + it("returns false when c7 key hash does not match", async function () { + const revertingVerifier = await ( + await ethers.getContractFactory("RevertOnVerifyCircuitVerifier") + ).deploy(); + await revertingVerifier.waitForDeployment(); + + const bfvDecryptionVerifier = await ( + await ethers.getContractFactory("BfvDecryptionVerifier") + ).deploy( + await revertingVerifier.getAddress(), + EXPECTED_C6_FOLD_KEY_HASH, + EXPECTED_C7_KEY_HASH, + ); + await bfvDecryptionVerifier.waitForDeployment(); + + const messageCoeffs = [1n, 2n, 3n]; + const publicInputs = buildPublicInputsWithMessage(messageCoeffs, 402, [ + EXPECTED_C6_FOLD_KEY_HASH, + ethers.id("wrong-c7"), + ]); + const plaintextHash = plaintextToHash(messageCoeffs); + const proof = encodeProof("0x01", publicInputs); + + const result = await bfvDecryptionVerifier.verify.staticCall( + plaintextHash, + proof, + ); + expect(result).to.equal(false); + }); + it("returns false when plaintext hash mismatch", async function () { const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, From ad7e352f404794e75c41de69afadfb06e1df127e Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 13:36:22 +0200 Subject: [PATCH 02/54] bind to on-chain data --- circuits/benchmarks/scripts/run_benchmarks.sh | 1 + .../src/threshold_plaintext_aggregator.rs | 17 +- .../registry/CiphernodeRegistryOwnable.sol | 3 + .../verifiers/bfv/BfvDecryptionVerifier.sol | 6 + .../bfv/honk/DecryptionAggregatorVerifier.sol | 1365 +++++------------ .../bfv/honk/DkgAggregatorVerifier.sol | 1365 +++++------------ 6 files changed, 788 insertions(+), 1969 deletions(-) diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index c5a9a9f5d..466d87c17 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -163,6 +163,7 @@ if [ "$SKIP_COMPILE" = false ]; then if "${SCRIPT_DIR}/check_circuit_preset_artifacts.sh" "$PRESET_NAME"; then PRESET_ARTIFACTS_READY=true fi + "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" echo "Preflight build complete." echo "" fi diff --git a/crates/aggregator/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index a2bd99f04..08aba72e7 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -21,7 +21,10 @@ use e3_events::{ ZkResponse, }; use e3_fhe_params::BfvPreset; -use e3_sortition::{E3CommitteeContainsRequest, E3CommitteeContainsResponse, Sortition}; +use e3_sortition::{ + CommitteeMembersResponse, E3CommitteeContainsRequest, E3CommitteeContainsResponse, + GetCommitteeMembersRequest, Sortition, +}; use e3_trbfv::{ calculate_threshold_decryption::CalculateThresholdDecryptionRequest, TrBFVConfig, TrBFVRequest, TrBFVResponse, @@ -1074,6 +1077,18 @@ impl Handler>> } } +impl Handler for ThresholdPlaintextAggregator { + type Result = (); + + fn handle(&mut self, msg: CommitteeMembersResponse, ctx: &mut Self::Context) -> Self::Result { + self.committee_members = Some(msg.members); + if let Some(ec) = self.last_ec.clone() { + let _ = self.maybe_start_decryption_aggregation(&ec, ctx.address().recipient()); + let _ = self.try_publish_complete(); + } + } +} + impl Handler> for ThresholdPlaintextAggregator { type Result = (); fn handle( diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 0773230d8..4456b4d46 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -212,6 +212,9 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { bytes32 committeeHash = CommitteeHashLib.hash(c.topNodes); c.committeeHash = committeeHash; + bytes32 committeeHash = CommitteeHashLib.hash(c.topNodes); + c.committeeHash = committeeHash; + E3 memory e3 = enclave.getE3(e3Id); if (e3.proofAggregationEnabled) { require(proof.length > 0, DkgProofRequired()); diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index 579ffae4f..045da86af 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -45,6 +45,12 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { /// @dev `4 + DEC_RETURN_PREFIX_LEN + DEC_RETURN_COLUMN_COUNT*(T+1) + MESSAGE_COEFFS_COUNT`. uint256 internal immutable expectedPublicInputsLen; + /// @dev `publicInputs` index for `committee_hash_hi` (after sub-circuit key hashes). + uint256 internal constant COMMITTEE_HASH_HI_IDX = 2; + + /// @dev `publicInputs` index for `committee_hash_lo`. + uint256 internal constant COMMITTEE_HASH_LO_IDX = 3; + /// @notice Underlying Honk verifier for the DecryptionAggregator circuit. ICircuitVerifier public immutable circuitVerifier; diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 110829355..b9ad064b8 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,11 +10,7 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -227,13 +223,9 @@ library HonkVerificationKey { 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -251,31 +243,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +406,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +604,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +638,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +650,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +659,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +675,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +696,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +714,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +725,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +765,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +792,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +809,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +873,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +891,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +950,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +969,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +989,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +1000,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +1019,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +1064,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1204,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1212,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1226,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1236,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1311,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1336,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1372,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1386,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1408,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1417,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1461,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1510,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1571,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1602,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1623,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1644,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1680,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1702,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1736,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1759,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1806,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1822,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1834,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1854,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1900,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1924,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1953,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1977,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1989,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1999,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +2019,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +2039,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +2063,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +2095,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2124,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2147,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2159,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2192,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2212,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2273,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2287,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2342,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2359,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2381,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2405,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2424,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2441,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2483,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2493,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2504,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2529,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2539,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2553,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 8f8d5f63f..608c9fba0 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,11 +10,7 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -227,13 +223,9 @@ library HonkVerificationKey { 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -251,31 +243,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +406,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +604,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +638,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +650,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +659,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +675,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +696,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +714,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +725,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +765,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +792,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +809,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +873,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +891,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +950,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +969,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +989,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +1000,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +1019,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +1064,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1204,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1212,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1226,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1236,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1311,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1336,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1372,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1386,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1408,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1417,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1461,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1510,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1571,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1602,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1623,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1644,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1680,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1702,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1736,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1759,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1806,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1822,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1834,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1854,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1900,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1924,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1953,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1977,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1989,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1999,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +2019,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +2039,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +2063,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +2095,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2124,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2147,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2159,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2192,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2212,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2273,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2287,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2342,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2359,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2381,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2405,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2424,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2441,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2483,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2493,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2504,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2529,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2539,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2553,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } From fe70d1b1b8e9b39eea0eb16211b8099ab217dc30 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 13:38:44 +0200 Subject: [PATCH 03/54] fmt --- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1365 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1365 ++++++++++++----- 2 files changed, 1968 insertions(+), 762 deletions(-) diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index b9ad064b8..110829355 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,7 +10,11 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -223,9 +227,13 @@ library HonkVerificationKey { 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -243,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -406,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -604,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -638,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -650,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -659,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -675,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -696,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -714,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -725,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -765,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -792,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -809,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -873,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -891,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -950,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -969,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -989,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1000,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1019,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1064,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1204,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1212,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1226,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1236,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1311,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1336,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1372,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1386,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1408,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1417,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1461,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1510,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1571,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1602,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1623,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1644,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1680,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1702,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1736,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1759,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1806,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1822,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1834,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1854,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1900,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1924,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1953,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1977,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1989,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1999,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2019,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2039,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2063,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -2095,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2124,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2147,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2159,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2192,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2212,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2273,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2287,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2342,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2359,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2381,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2405,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2424,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2441,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2483,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2493,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2504,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2529,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2539,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2553,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 608c9fba0..8f8d5f63f 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,7 +10,11 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -223,9 +227,13 @@ library HonkVerificationKey { 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -243,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -406,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -604,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -638,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -650,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -659,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -675,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -696,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -714,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -725,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -765,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -792,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -809,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -873,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -891,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -950,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -969,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -989,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1000,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1019,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1064,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1204,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1212,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1226,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1236,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1311,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1336,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1372,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1386,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1408,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1417,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1461,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1510,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1571,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1602,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1623,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1644,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1680,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1702,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1736,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1759,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1806,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1822,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1834,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1854,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1900,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1924,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1953,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1977,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1989,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1999,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2019,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2039,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2063,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -2095,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2124,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2147,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2159,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2192,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2212,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2273,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2287,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2342,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2359,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2381,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2405,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2424,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2441,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2483,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2493,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2504,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2529,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2539,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2553,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } From 089fad5482238692f06b2fa864c9c60c05d71c62 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 14:10:41 +0200 Subject: [PATCH 04/54] update insecure benches --- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1365 +++++------------ .../bfv/honk/DkgAggregatorVerifier.sol | 1365 +++++------------ 2 files changed, 762 insertions(+), 1968 deletions(-) diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 110829355..b9ad064b8 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,11 +10,7 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -227,13 +223,9 @@ library HonkVerificationKey { 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -251,31 +243,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +406,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +604,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +638,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +650,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +659,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +675,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +696,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +714,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +725,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +765,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +792,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +809,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +873,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +891,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +950,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +969,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +989,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +1000,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +1019,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +1064,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1204,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1212,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1226,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1236,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1311,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1336,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1372,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1386,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1408,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1417,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1461,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1510,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1571,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1602,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1623,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1644,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1680,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1702,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1736,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1759,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1806,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1822,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1834,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1854,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1900,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1924,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1953,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1977,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1989,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1999,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +2019,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +2039,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +2063,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +2095,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2124,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2147,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2159,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2192,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2212,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2273,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2287,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2342,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2359,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2381,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2405,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2424,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2441,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2483,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2493,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2504,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2529,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2539,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2553,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 8f8d5f63f..608c9fba0 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,11 +10,7 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -227,13 +223,9 @@ library HonkVerificationKey { 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -251,31 +243,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +406,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +604,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +638,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +650,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +659,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +675,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +696,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +714,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +725,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +765,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +792,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +809,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +873,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +891,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +950,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +969,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +989,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +1000,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +1019,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +1064,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1204,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1212,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1226,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1236,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1311,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1336,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1372,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1386,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1408,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1417,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1461,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1510,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1571,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1602,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1623,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1644,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1680,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1702,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1736,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1759,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1806,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1822,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1834,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1854,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1900,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1924,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1953,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1977,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1989,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1999,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +2019,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +2039,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +2063,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +2095,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2124,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2147,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2159,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2192,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2212,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2273,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2287,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2342,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2359,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2381,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2405,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2424,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2441,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2483,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2493,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2504,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2529,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2539,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2553,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } From badb2b1481be5f5531b8d2cb694dabb51c8efc20 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 15:49:13 +0200 Subject: [PATCH 05/54] address hash commit issue --- .../decryption_aggregator/src/main.nr | 1 + .../src/circuits/aggregation/node_dkg_fold.rs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr index 67f6a513b..e354a0764 100644 --- a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr +++ b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr @@ -15,6 +15,7 @@ //! supplied in the same order as on-chain `topNodes`; the hash equality pins that order. use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; +use lib::configs::default::H; use lib::configs::default::MAX_MSG_NON_ZERO_COEFFS; use lib::configs::default::N_PARTIES; use lib::configs::default::T; diff --git a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs index 87ef773a2..1c71ef037 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -389,6 +389,12 @@ pub fn prove_dkg_aggregation( .map(address_to_field_hex) .collect(); + let committee_members: Vec = input + .committee_addresses + .iter() + .map(|addr| address_to_field_hex(addr)) + .collect::>()?; + let witness = DkgAggregatorWitness { nodes_fold_vk: nodes_fold_vk.verification_key.clone(), nodes_fold_proof: proof_field_strings(&nodes_fold_proof)?, @@ -487,6 +493,11 @@ pub fn prove_decryption_aggregation_jobs( .map(address_to_field_hex) .collect(); + let committee_members: Vec = committee_addresses + .iter() + .map(|addr| address_to_field_hex(addr)) + .collect::>()?; + let mut out = Vec::with_capacity(jobs.len()); for (i, job) in jobs.iter().enumerate() { let c6_fold = generate_sequential_c6_fold( From 463817bc78a4790162e4a63df6d7cd7a4f1a78df Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 16:15:33 +0200 Subject: [PATCH 06/54] address coderabbit issues --- .../src/threshold_plaintext_aggregator.rs | 29 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1365 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1365 ++++++++++++----- 3 files changed, 1994 insertions(+), 765 deletions(-) diff --git a/crates/aggregator/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 08aba72e7..98662f5bb 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -1081,10 +1081,33 @@ impl Handler for ThresholdPlaintextAggregator { type Result = (); fn handle(&mut self, msg: CommitteeMembersResponse, ctx: &mut Self::Context) -> Self::Result { - self.committee_members = Some(msg.members); + let Some(members) = msg.members else { + warn!( + e3_id = %self.e3_id, + "committee members unavailable (E3 committee not finalized in sortition)" + ); + if let Some(ec) = self.last_ec.clone() { + let _ = self.fail_decryption_round(ec); + } + return; + }; + self.committee_members = Some(members); if let Some(ec) = self.last_ec.clone() { - let _ = self.maybe_start_decryption_aggregation(&ec, ctx.address().recipient()); - let _ = self.try_publish_complete(); + if let Err(e) = self.maybe_start_decryption_aggregation(&ec, ctx.address().recipient()) + { + warn!( + e3_id = %self.e3_id, + error = %e, + "maybe_start_decryption_aggregation failed after committee members response" + ); + } + if let Err(e) = self.try_publish_complete() { + warn!( + e3_id = %self.e3_id, + error = %e, + "try_publish_complete failed after committee members response" + ); + } } } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index b9ad064b8..110829355 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,7 +10,11 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -223,9 +227,13 @@ library HonkVerificationKey { 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -243,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -406,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -604,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -638,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -650,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -659,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -675,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -696,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -714,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -725,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -765,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -792,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -809,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -873,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -891,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -950,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -969,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -989,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1000,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1019,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1064,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1204,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1212,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1226,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1236,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1311,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1336,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1372,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1386,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1408,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1417,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1461,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1510,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1571,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1602,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1623,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1644,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1680,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1702,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1736,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1759,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1806,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1822,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1834,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1854,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1900,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1924,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1953,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1977,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1989,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1999,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2019,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2039,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2063,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -2095,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2124,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2147,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2159,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2192,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2212,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2273,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2287,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2342,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2359,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2381,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2405,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2424,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2441,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2483,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2493,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2504,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2529,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2539,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2553,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 608c9fba0..8f8d5f63f 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,7 +10,11 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -223,9 +227,13 @@ library HonkVerificationKey { 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -243,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -406,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -604,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -638,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -650,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -659,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -675,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -696,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -714,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -725,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -765,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -792,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -809,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -873,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -891,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -950,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -969,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -989,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1000,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1019,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1064,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1204,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1212,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1226,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1236,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1311,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1336,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1372,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1386,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1408,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1417,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1461,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1510,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1571,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1602,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1623,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1644,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1680,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1702,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1736,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1759,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1806,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1822,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1834,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1854,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1900,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1924,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1953,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1977,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1989,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1999,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2019,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2039,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2063,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -2095,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2124,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2147,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2159,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2192,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2212,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2273,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2287,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2342,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2359,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2381,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2405,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2424,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2441,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2483,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2493,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2504,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2529,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2539,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2553,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } From d0386929859e79ef80bcd89473b4140584299679 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 16:42:52 +0200 Subject: [PATCH 07/54] fix changes --- .../results_insecure/crisp_verify_gas.json | 47 + .../decryption_aggregator/src/main.nr | 1 - .../bfv/honk/DecryptionAggregatorVerifier.sol | 1415 +++++------------ .../bfv/honk/DkgAggregatorVerifier.sol | 1415 +++++------------ 4 files changed, 839 insertions(+), 2039 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index ee6f0369a..910381bf3 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -74,6 +74,53 @@ } } }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.113354416, "runs": 3, "total_seconds": 0.34006325 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.612437277, "runs": 3, "total_seconds": 1.837311833 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.565879209, "runs": 1, "total_seconds": 0.565879209 }, + { "name": "GenEsiSss", "avg_seconds": 0.126440847, "runs": 3, "total_seconds": 0.379322542 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.230296195, "runs": 3, "total_seconds": 0.690888585 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.51343, "runs": 1, "total_seconds": 8.51343 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 51.1417945, "runs": 1, "total_seconds": 51.1417945 }, + { "name": "ZkDkgAggregation", "avg_seconds": 21.243403209, "runs": 1, "total_seconds": 21.243403209 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.512020374, "runs": 6, "total_seconds": 9.072122249 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 64.910513486, "runs": 3, "total_seconds": 194.731540458 }, + { "name": "ZkPkAggregation", "avg_seconds": 2.19326375, "runs": 1, "total_seconds": 2.19326375 }, + { "name": "ZkPkBfv", "avg_seconds": 0.347720638, "runs": 3, "total_seconds": 1.043161916 }, + { "name": "ZkPkGeneration", "avg_seconds": 1.388650764, "runs": 3, "total_seconds": 4.165952292 }, + { "name": "ZkShareComputation", "avg_seconds": 2.774372228, "runs": 6, "total_seconds": 16.646233373 }, + { "name": "ZkShareEncryption", "avg_seconds": 2.591194588, "runs": 24, "total_seconds": 62.188670125 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.166238347, "runs": 3, "total_seconds": 18.498715042 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.096496916, "runs": 3, "total_seconds": 0.28949075 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.234150691, "runs": 5, "total_seconds": 1.170753457 } + ], + "operation_timings_total_seconds": 394.71199654, + "timings_seconds": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9 }, + { "label": "Setup completed", "seconds": 3.078788792 }, + { "label": "Committee Setup Completed", "seconds": 20.221637667 }, + { "label": "Committee Finalization Complete", "seconds": 0.006574583 }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 314.973630875 }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 317.623802666 }, + { "label": "Application CT Gen", "seconds": 0.315554541 }, + { "label": "Running FHE Application", "seconds": 0.003503458 }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 81.660311958 }, + { "label": "Entire Test", "seconds": 422.916649625 } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000ec9ef698121a5c39000000000000000000000000000000000000000000000000e41f942fce4d490eb00000000000000000000000000000000000000000000000195fb4f10129d1d78000000000000000000000000000000000000000000000000000096d82d22f57a000000000000000000000000000000000000000000000009954ce731edb6dd3c00000000000000000000000000000000000000000000000c3bd0ec9dd55b67f0000000000000000000000000000000000000000000000003728adde765d0e1be00000000000000000000000000000000000000000000000000027b59548a2ec1000000000000000000000000000000000000000000000005347bf3da34ea673100000000000000000000000000000000000000000000000d629407ed2ca5889600000000000000000000000000000000000000000000000d9316749580d8335d0000000000000000000000000000000000000000000000000000451a30b1de5100000000000000000000000000000000000000000000000c16172d7eecd0619500000000000000000000000000000000000000000000000adf01248f5b8c94e200000000000000000000000000000000000000000000000859ad32a233d1424d0000000000000000000000000000000000000000000000000001e8827d8f4dd91d0bf0d7bc3141a4dd134caf21d77bafd25e2801bae69edef7950fe1c8cf0e1c05fd9ce40ef8073d0e9167a762a736f4facc1815f0d8163bfd93cc017eca42fc0fc496da80095322fc75820967dda64c09ac96275567f11e014fe874e35f4c1b1c6dbbd70d14d031ccf593c50dddda968df0f26bcd235d9bfd78d7fadcf5dc4c082c8650c77afa5be438815c3d42ea08aee11084cdfa6acd4d46d109ea7bc42a2042981382bef951d645feb6dc93d46518f03db584013c33209e0cfd8165016716707faef9290e1f2d5456366066051d4a5b1ed85c9b56b2f3a767c8898ae08819fa1d753808dda784dc6ae81116c5984b2e3f7cd8de51a83adb0afa9b3ca6432a1fae1883236c06bc4f084263a4df184700231ae641d20277f6e7626fc026c52dc7e925492285c0754912271f4b382112f5a0cf23229fa89f6b1a66c5caacd114260f3e6d1f10af300e1843b16413df24e32bbad69247f23fbd482469a12cc72b45ef9117ab888d998d42e1d4cdcdd8fadf0f4c0615a748d25a80b35c7b14a408ca6b5c9be88eb9f015340260f7ac3d4b702fa29ea583c12aedde88a29295d507a99c79637f9e6e508b6e2cee68bcf5a4d55b996096e9958ed14b64160422fc272fac98173a2b2fe6c548b7cfec9b9936a29adbdd9d16250226e77c797b945a1d98a9dcf2a7dba90f9326de6dbc89a442570e30f26606652826872b79016e3c26d7da27f6178484f07514cda21d42c6bcf42d2a77bc8b4f36e5f2c6d29d063b120b810cc1c1f2e69ab126edf280494d9a9c94507eec1f37d5b66b71bc5a23e6128dbd86ba4a243a9a0cacf72100f9d43b1e8a3dd25349e062e3b29e4ca4db2f23d46ec0a0321161ea953620e0d0a400bd522610d2bf3a1d1f4e03408280a4eb01f2c96de382a1d4032e1a201afc51eae9795b8f229e48abd893c57eb94f7a512a65956c2aa748f1afb6f0946d8ccb60fc874da7ff480e945a506337c8e046320e2aa705ddc585323194799593e9e5d359fd88a17a9f65187cf0210940e1659a29563411bd539cb6be00d056442c9e842afbbe75f44280307f3666287e0f21662f49976c867a62e4c2c3a24b11ffcc6b043a772418bcff0b68f9946b9f9c73b42fef2ffd192d0fb4e9601d72ebb81253217d473ab13967c8cf0673928c6187201c9b3f5d4aa22cee646950cdcda3da589539df5f1aacb1d2725dc60344ee5d9728abef864a21f93aeb0016e4b9a9b0968fbdab4e601b8f88bfb18de531d9f78019d8b1ab781050c8fcc4062bcf4bce74df6ce2861013af9fca034ca763399e3010091a5bf71607501a0d61e999341708104b83ca21703421c2e400192a1f21bd0c1c673628d372ec09c56d244a04560e265c971cc505e31b30fdce03472507dd0610bf3ab5bbb3bb5d6bdc5ca8cbd82afb058dd7baf634f759379524cf80e7162ab4036dda4b58ad3781247af235afafd6e919f90dbb72ac2226f2f86bc2df1c008f6ff95a49c89d880cb6af606a4ddf7e9ccf097b1c4b946a187f90555d393a11a1d909a57fdfb30a8410180cdb113ccf6fdb0a7fae7281c76e49c0580bc3160d77aa58dbde5ed1d51f402a0fce68badbd10cbe75c128fbcf22cf53e2709eab0834a0e5526c91fbc6b15c1b5bfead10b8e6d0ab3171b2d8a106c3e4fd026a7a01e840edb4e03449356fbcccdffca3a45a59afd472fc980d83fc1504e952a2790013006ba1e5427ac816f25adae5fff4194e25659862be62b0d43b8961bc224f11ccf3bd16f35538162c261e65048d9e4e4cc70286af0eac700570096141331b0be19c23a4117d1bfadafc509612d2d774c13810ecc791068dcda29d75f889001ab891ba8fe14dd2d11f05b422d7484fae0de35e39b6b97451cb4dc12cf5694d0f8de2e3ee7e5aaae8cfe3633efb2d01b1cee79b870603ddf78d339a31238439000a22a5dde7d7abf2e0f72fc8806d086d73f98f3202b454db461aae23c7a8a80b51da3d101f0721ac69e21cf9cade88a606ddc8f8b4f9c0d80f12e5fe750ae6200cded97041e21930cbdad1c0903302950346e6a3861935a0208dcb71ad1c5a0d627447005be70389df3998f482d674f40ce14592c3407f2eb19213c436835406590d2867f21c316ad81b9fcaa64fb2356cc4023c282b8fcfca989770b2ad551a20ce681a2dbcbefa6c08c847e87bc3a307e36cdb61cddeed0f464b27e0fc86290793fbb62b364038dd10ae3b7f13b856e9b0b427cfedd8211248b16e392f4f04b4d3c00630fe829bd6e54d4e1eacce1c37cdd9c6ba7bde51e8ae7591df978729a97138329eee41221d97413dfb35c1178d301098d2972ef49c9d3d37ce8ba92142609dce818d3dc272faef7a9d6739c9a2329b3ae8b741d1cebe2d482868630fced55ac41271ea90925f69eaf5f506bf915a7b60c7b1cea5a8814f3dfb2daa2712c16feeed6b5bd73bb8437f0b33a2b2a09557f97649370eb9c15edffb6cfc0d451a3b62f6a1582ef86137cf115d72346fedcfdd09cf2057ba0ff3d50cb3c5045c72890b5ebd82af87b0a950c802d4f8372fb67d9cba3103e4c3c21b59e887164a23970149af17c8074496fee140c989307fef339b5846f4fff60ea4771b6f1b627b1c3c5e6534abf8e1ceca32fb8d2f7256135e3a2a905a65c7c4900a5eba009da53c08a8264c2d95a8e9e0e66129704ed450139855b738a57f2f8e4838501d88faf34c798100dcceaa6ba31a844742bb57d646b8403f5d9bb8f55f0c3626150d5018b6695de63c4c6346253f531674c1f6083cfdba94c3e7281a0de65c7220681300cc6b94580371624a9a778201db61e5d292c9984c040d8a2bdaf01f6528196094e94b726e83f8527b0860b11ba1cf78a2c1c5c72f85a57958a2689f361e2e867bdd4a28e7ba5be4fae95e5e66d44caabb18509df95c5e8d04060619da0302378d130924320c43337c2b9f71339261401d608ea2acbcd25fcf47f7959001676330b04e5e7df983712fd2ca5159de2b2d2e9dc0a3b1e6dc022fd21daa9b26f45730dd7b23fa1299408d88269521da1225e7e8fc4aac0b0fa30375a218de0d97ca096e738e1c890cb57b3c4fe078a1251f33d24e52695ca0fe31b93c04060eeead6540e060ac7adb14f6719ced88e631fc70ba50f8ca8e5be17c0d8716f924e7bdb0daab87e3b6901720eae49e7ed230828c54c597dbc024797cd024429604a1ce139df61c37b81d6326f880a4077129e81a08a92be8bbb7375805a0b71409c44c049dfb0093e06392154a6742f353159620f2e94f83e3dd9a069a8416fe1376e6fd487a4766af44cef2eab670e550c8fe51df3424de3c3e215f9a7e8a3106073d4c626b7a496930e0e59a8898fe992a41604af683ccb5a29e6e7840a4260c5ffdfb5a3699688ba4808fe5eb9bc44bca0ff5979693272d9a1c362b9843ae28c6fc1f51cd98c5c2da4242c9f650d5c267e513d8208a40df4b7dddd14e789009a8274b38ce0c3c928b4927b9548b283ad304295c0598dfdb0ba0ca3c96b5d8094f587b973d1425a3746abc0daae7480fda4a2bbb2ddabaf50c5770f7291a950edcd9863a06cf513bd75c3616ada207dff99bc316f83d7d1156c2ef24c9e96c11c9337457ea5b6c6baddc66818eee1decd61ffd15b5cb5cb7b5a9eed72a4c5406b832c1d539589d402db21f7b1ccbcd9c5272379ea7438bea5f9d8f2a5e5739057112baf67d6070c3f347e349e0fb06cf579b9a8f289ce45cfe74635d5253980a126858ab7d12980496c06a7b7c6a95e7f6e4ecfa2957c77da9748cefc08d8618875439e9e2168e7e05a1cb42af517def29aab9753a568f465bc9e1378028090e0e94f05fa827a7590b317f23227190c6b01454f6acc256311dcb76b3033fa7064fed92f22742923d2974e504ce93711614bf5c7edc00c0a3b8cc38c014317704e2d1dce3b2a9fafa881c14edc39a36bc545db0887c9475f53a7ef5eb32f90b1e87d8f50fa529f18eaabb7b84ec1868f9eacd9927431091cde1044ffd78afca2563fa2c2917e76b9aeec95bbf2c2da274934c839ff2466e449ef0aabb5a444c296c050c8c44966025cdb1b8fc46bbb208c3a372758c440f4e46069ac0ff2e5c1e9e0bfae61ed536008d9fbfe946f189eb66f0395dd51247f73f0c18167f594b1437ee2fd4a565041f523b89e595c81ae1f54d72f42f538b8dde9b8989aa532b0942d496965da36cb461dae9ddf7110e2440295e1cc589240f54c0007585df172c1e3279e05a2d616620ce42c5e923b8b41fae97c732cf92c1efe28d039d673d1acf1670740612698be6eec0d7794d28c2215f2539f181a4547e7a027cda29682b0271548cda6b0d33b33defcea7fbe8208e11f22fd00409920c98e408dc84911f3be215ee39f6d6cf11eb97f9a1552dea6e333a850eda7a5865e446d39451a91ebe76a8e4991a190efb68b81ed74f6b9fbe5a9440f278e006bf4a050caf74ed29ce83fa8130bba02eb6d8502546641804143dffadaae81a48455c942abaa3f92ea5dd70c89b99cfaf9039646212b32dbc450fc6c155ae7e54e69feafa93bd480278aff364713a492223b70d38acbe5b15791a0391e5a90259d10af8d93ce96a0f07d35efd224a4afea83eacb73c5cb3fc61a66b75551deb76c9db8f207f0e9f1377dd7b8cd97d7411b11fc2152d21d6e04591ca623d4ab2e5f1164d6a95896f2212ecb096f1e21d1492b2469bbe96ffcfdf77502b7338122562cb390c30752f0d1459977331a5b65fa24818be9c9a0b01e5d3652ce97ab8eda935aa40935ddb0d7e334f37a1e1e9a24428e5773c01db1495d15f23b41f95113f3e7cbb2b64b30998b4e26a623cced0c6c246c2421d7a354fe1cf10e574ecdfe20887bdeec0ff2e9f7d479d603603479d950bc5b65cdec41f175c50e12fc2f10d513693da8cba09be3be16b55a0b63a2f6c83cdde050b3e979a19aa88efca5544d356591fed7122e8585a390e81386ec788812fc378251cbcca61186f818d764c5e3ac2ff651e2b1b84df02745bcd24956724035b2fcbb13b09e6641ed6980ac44f5a780fb875286cdec284ea9959449fd7a72aea4d800927d9c1bcaf02e28181b58536990db6030360c87597c8ab02eedae23a1cbff1dd4d17dfd9b3ff44f5bf139b63ce10f51705255ba9ad0425f329897bd4cff19afe6ba0a77107f212b1b00a40861af4df1cdcc6665a294a4d1f020d3ce73fe5094fe5384dcfa10677dbd29fd8669af5e223ebe866f0809bcc298055e9f9dc847dbd9722f94c8e7eede324ec82cda66491026afe9a61d8567e3b887aeebb05593a672d6835b574a4b3ca4d4f3cf5e99f282b55d6f90dd66f2417dcf5b93b85d5f63d7a88bf7e11851e7f811c920eb9833a149fb762c1330b97e0a9dcfa44f1fead5d80dd39c321635bc63534971f52453825ed4c0a060d6a421b5601c8ab40eb8aaddb993a06c759e51841e94547742aa923eaf9bb18f29a544fae4f5349e1df1dfb84399b8b21a746775de962d5d2f0461273e8c8a1b50db92053f02695053619ab0ee4b7d36c46d489b9f6e3c5a4d15f256a827b4c38c9be76860729c4627c31ec725e74ec35b29b6fd3f225793903ff04932416ab27e43368a89ab6f380a76196c1a282e65a5496624a3f8451d1d5392390fa9bb3aa8dc264a0cf6d92f276ec585abd770d5e5a515c20c2e8127dba660403190dbb1cf5a965b95d9cb997676fe763ebc5504b4f33a9e67aab1b3017800b399001da114a5958d9c0997270fa455ca54c44328abb04ad1d38eb1ee5d92e1d848d673315acc09e92178b36c42336cba10b15e42ab30533aee5d6207a23fa06cca99cbe2e1e5ba0a4c48a20df0ba9f7bdc6e4865d24e657de9f8e74772ec323f94d7faadb7609d70896ad03b3873e7312de54ce84a0d25977a186e5d36fdd04826858efc95f0e725c1a0170f90126e28218127098fccc6f8e93bbc5dc94a42d93b194a264619494f32d24c66f7d74266ea517c85acadcf79d983a00c58f9d3021f4345b0d3bb028c12bb97fd98ccd2085891e183adc9b7bcf30d7f7a16b8f128c35858ad8953b3e8c1112282650e531e94f151f3bc9e8bd392184b82919d518584dfd8ec5ccb00e67b92d22da2594b81e7338ea8e17fbc5a366a20b28d6b2157ffc8ec16bb4a79738cc95b88255eaba014f04d4f8ef8a1b3f837c6cc2f26f2deda5477f7f3681eefea8bda62548d5d8aa1ee1283ec16f53e4f3df239a12f403dc3aa82aaaaf4fce71f168a3c9bd12138fef7f2a75bb250a84e5b3dd2b69771a3ec4561abdd4dbcbbf63daeb7ee1fbfaf8a2e14c8e63fdb33e8ca97fbd8dde2c3a64777200326b95cfaca821f536be5e7103037b42e6db4b8468a2518439cf17b05607a124eb2ddf7ea1ef3d0926610263cec35870f64d736cb576bec97ff02e4f1b460d3218f62b5fed7ef4d5734311f0d96370538c1e0d2a3456203a4da1149b20f6f624c1a5eb14f3b6e23dc5703e6a0639c4f4295421fafaf44b43f3a3295139e08f2740ee9928e679c015b16e378e0459daecc0e1289990dc3c43d57d2ce7adf15e252b64728e4eb4474e069291886becc5e3a7f3f7a23971914acab50999b3d1b1247a99b4d476acf564ec63978549733d80e5c89f29ed55df452acd2572934bd1fed33bf7a38dc15eec7a58aa24266f3269d64789f092e7f2226084058d43881ee24895a4ab41d6df42c1fc1adcbd8dd364a4685fa6f785590c98ec2e859091087d624d7953bc8286b9e75152cd225c2ded34a72de786d003f53ed305d0e276b613bf274ddf18d47d8671e1cc36053ab1043b7bedc8c8f73a51eaac1a92867b56d997ada05774fea9de9098df706bd127ae6ead366acae1e70df2100403c7eb2d5b35a5025f1cb28d1d658ce39dca67b38430c42a1f6476135514420ad054a7fb7658417a95ca357ee3371d5243796fcd6ef5d3e756fb3fd388f4561fca53dfc96f682133bb48a7b345c1111cc9af0516b67195b851222a50bd0f9e010c08f4218d579c9e5bf4d34984f3e67b76f59a09f36c0845b5fb7c7de8f516034ed58397993debe05f425882b5356afcd858bd95f36d21d30a8cbe81b5a433006813e0a75c02e31ce51ee8e4bf5e9db6b9bb4da9e8ff7cd7035a2fe9b29c1603574f3c14bf0969e84c55940ce0507d8ff40a14d3a3807c98aa06d20addb2961a35842b5d31175d70914bbac65f30541fd7939d8476d332481bc935db7686b40d10706636fe09879dd9c78b5d966f85346262cec45b9ca953bfce5754a973482191b13b36cdb489638d09c696f9789ca8ce03bd3154563073d0152b5f7938652e7e872589bf6af3ba89d6a643a1d628c88b615b3f9ee38a0415dbdd0fc7f4ae07c69d33bc6e4ff89d9c048f3040bfb2818a0327b6241aa91277ec984a54b52c2c4134c0f6765334c4b9550c24e031cb1a0b08ecca7e8acc43d3da56e06dd3222fbd07388306b44e8ee96acc822500c3bea60f0ec5ddc469b085f040ad7435af19c3b6c8c584c648f81a3c1532af5bfaa4d9f7800b1ac53726a4209ac340d8f00db7bf471cc277d7bdaba3b289cedb5d29e8b1a0de6933389bd97b838b49b3a02d99764a4165f2cfa8a42dfc3eb407ea37372abc442d49513d1a248ca15837d7200c1eeba96c6dcf63aff0c43912249be0de697e8216ec83c7014b3890379a361c60c5cab97ebcd3a91c63be61161d5776fe04a0d4b7794547d58434f22b1d2e01df5c3496479fb742fdb383182a0f82806f034a73d11ddb26c29a113f41bcca286191a6537e9e2465081026bd75f3d8004fd720005839243a73636179bd8eaa16f74b44f5598ebad7ad0ad6edf8518371752effbbb0d9158cde2adfef8aff80028a14d27017e83282e4a58d3aa87aa413c0d1b455efaf476696989d446625c4104a26b0d94c9660c31cc3fc65e3c4df1ece6207b1411b125cb84e3557d226731a7a20b3f7a5882a6ab25d92c8599da8a7d0f6d4e6639b834d5a1ad0369d2f45127cb1c3222a85abae4598e65930dd8d0470cbf40d2041afed6884134142f1d0249afceffd4a23865d3bd073b67004cd5390222d061931e6053e354a0ff761cd258c36a285bc13906e3a4fed7c07298421178922838ec126cf41c69ae2c9b92619ab4f4d85454d3a1194383446a73388cca8df303e88c55dd395632e9fd30d18216f670538dba3684ccfec7bb4635b2a8d22834b0bfa34b5b20bf49939ec1ef717f399c44a938dd4db05855c2468e3575d93d4ef586d759e572930b84e7ee1dc1fb18a2977fca5896ac9a9b11ed5a95856de6b617a0144867fed36d3e7e8ff73043dcc0f89c5417055cc4c742a0077d552bbe0621f5d8149a525eb2950d8c1d720c6f766e5e8939ca092bee411d46b2a87156db048d1466f97ec9418860b4f810ecf93c938c1bb700075d96d0ee33df8c0f1ed946d36339440a75ceeaf5e4877057a7f59931eb2dfed2c25cf8c8c1304104f31fecd29ae0bcd927db54898e4a111ffb3116d03b9acd3afab6184dde59ca077660c0371dfda948a8002dfbd058706933c4b4d9908ad759dc904e51946b76a5867436f005a2d19ce71996a520d6a195103257550171b731843e416bef558014c1695d5088df4724f7066e138d47711c8063c1316481f8d0bb12717fb97bac786a86b1f9a5671c563afe2f130da2d02b8a37417251824e2daa6c59778cec9ebf043455e64c8b0a90b89404b069721049b53b68565413c38334d8317b7e3ca57e03a76bc32da196655c744b65ce208094564dbdf75b7dd1ceed6e687cb6c33e991132225d05961f3b1286294dae60f2b5e916bcb4c5109e9cd706422eb5ae2ae1593dd462be1d3254de039b624537e24d695f9479cc9509dc1abcfd35053bbb1469148ab8b69535b6a24a7d32406ae264e670d49bea15796d45e6cbd3328d629ae50a7cd9e039e3aec8370985a2b1a0398016bf94ed111f901d936a82c85399301e266d506529a4e8cb8a156b512552524763d17dadf27538ba2a18779c5641f2ceef4e00f4acf96b96c3d223dbcf11a67e18fc6ee529f1d0c5ce12bca9dd5f1f8e4c3f9b05f097338fc517e55a08f0a29ef8caf7b0b3d6f8abc1f930a7367a40c58f1af0c9452f8e866c1be94cb1e10b8f25d5b84e601da02555eb3834514a9707ec8d17ce1b9f1fafa25fe8721152e6b294d391e83a358bfd180718df442d2341f7924da88c1f18773266dc326451aa57efabefbd9a5961c1b4bf59fc240a3b2fb88d10c8006e46433dbb521eb6b2af8e7808c4683783411ba2f259273a45714d3e431138dc1565c9a50d639956a080b639dec74c42cbf5a38ca79e6cdb2757e5bcd9fe18a421c48ee9088f87eeb22168ee57508a1a494ae8ce0b902f3d37dc799eaf69da83b70ff3b07aae4ba412502ee2bafa3e54e281a448e024b7a0cedf8b25ce751d0638d046671ffaba8f910955ba929a4aae6df051c7b89a70aefdb331f87191304d438517708a89ee55c276dd93baabb317b3c30e8406fc70f26ea09a18f9c2dc4b527a3171011dda53822ffe71aa39d9c61bb0d01668c7cb0791a26cf5a84a4e764cda8eb47af8ef0641e0407eb0363e9de0ddd095f007b7cfb3641afd06c355972e0c9d2b491a903410cdc1ce070fd0bc607a3e4db6c41bb13f8b2d2328013c608975b32ededbd44ed29b5ed7618cd1be2ce97d9c43e76b88e226cbf4cfb2c51fb6b84218336b8fd4c0d5bb3240dc9d465fe6463f8e0262479813fb30c001ff332babe68b455fb72710009d1841d10bbe3ddbcaaec39838218778cf0c5674d469f8a4efd30adad662f0fe5ae0ddb452d32153500cc27bd4412b66ec45e6fb646f17dd1fc81f7c73b842b4857e3052aee438d2c763e1ebbcbba7b7069bb6d823394f03cfa195a39f18d1658a78504eae6f75cc451f170758d7113541ccb415867d0b58beb96be221048077e7d29008740cb1b48a6def944e0459b21b3e1004004c9e179001389ccad591c603f2ddc61c9656d59c818d56b711e30bd2297566cb37b89a11022e3cdd6f71c229e7447a4c86292624b5b740fcd8dbc73bc1e46a015a4b3f98cdae09ae00b177767121241cb9ac8c79fc93278c12b96bb8e083aa11cb0498a73b1f5d37afe06705224f43e0f7d3b35a77d70b56660856e0d3cc857bfadad79fb9197b3d3cc242055656db132f6f02fda406a393c7dee9114efdce2457f5184cdb24b2005e200a08b50a61efe929580ec871a4c6e67ba64688becde11c210bdf24a631795da19735508bd0e3ca0ee697304516416446a044f7f727064d56488e407f86c1a490eed9a8e618a11a9409d570d687d160070f22b4f932fc86dd65fd5ccb3881d2f01bc09e3a463bf73d783f5213559fb74d00def06547a0012f787e490ef71ccd713ca081f1f774eac7ca1f375d5b8c3770ecb120d3206976c6e5e1d8cf53721fa152d8da9139720432fa6c991d33df3494cb3e28618cb99dd6b393307a243d59205d725f4a775f611ab67eaaf9eceb852924330b7a99b4a858938940ed5caf7be09bf3379a4987cad24280f7ee03cf166da0198732d8471be63261149d23f3c5f1d71e74558ee93e3bd8be503154ff8804309ee04aa82f19edee7d2d5abb503192ac2ae60b489fc6eab39860fbe9fea9b2b9dcd75c580cdda69e19d3a9f06f95115949d534059557fcc3647457dd0a51b99b5830c29713cc3fa6bec10a1cfcfae1da83004079637147a159c63f632eab4893aacde0642cc2d5a082f00c7f51c37210cb6c1e85b9bb7c30b9b6859ad823a2824244bb938d5700a34a82d02dd87300027febe85e66c00ad1f8ae3f4cb8705de7a307935a9b09c5f83b9cd6830182a2d5684ed85666bc9f259d55854b97bc7af5e5b0bab4fcbb50845cdf6120109591e0bb326e7bb1528a02dceafc2b2a35d747e1461c2790654cf1c28bca13446991f833ad980f9d49fe652de455e78b898729071dc77f08cc04e76d289fa594e8711e19a4c38ad676a582ee9143e91a782f326c22f2d1eeb069b7a97718275c67a1fdfd4d009c75184369d892b1c6d514d985221cb5206c7f68d3688562c543c782245aa3c0a6b70079f244538bb89270199b59ddde6820a5cb1af06b39adeaa131a84734c2ebbbfc7ec2fa961f6be227fa23f238bfc7a21b3d80b3a3886b492800cf524c843bb2e4015995a1d2fa9ec624c9d4df6754fbc0a05f474ccd63dc4a0012dd1099334568939b2be801baede610cffa142af1b033817c60d1976f6c15412d70ecc05267712f7c38d569db6ce29c2fa140e469353dfa14874aa9dfc60650e8e7bae67d9f7ba8c84d65bd13a40b609f899edc8feb8b8b1ac1579330a75bf2fe6869bcf2328f5e4e33b94bc6f2beaa92831115b6d467cefc8a4dfcefa31ac12d7f722e74af1cc316f18c08809f55c997be302ec77ed498890fff92f7a94581621fafa36ee7a1d850b6bb85afe296408dcc86c13ab680b9e29deabc49621fc25906893f898794b11682c468b198d9d5bc6ba3337d3b9aeb6c6a5e6745d3a31144d3023681f47ea2d17736cf5cbf1114af0039e9f8c5ab1a643a8b6a109189816a8492490080135728a91d1645c79e51ab15a41f354e0652fec1176cb03430f08623df28499bb77f02e5b1123e82b6756e026a4059fd0d3cf55fd8bfc9deb3210b0423c3a9db9283f97f4d76bfcb8eafb2e108a2e66cdf599b0c8e3c2bf6c6922b0a2b81f915c9d5f0434e366e54d6a4106ea972f53abe8ff8ffe0ad1eba1430cde400af6ebdc11f2e4e2c3d206ecbc19adf11e2adeff7fa31d917b55ea2bf00cb57c0a65f17ebb620679c389f99ade39a20505d0c6808a6cb7a0fd593766df227df8dbfa84c24339f15354eca15dd767e20c29554da57c66b67707105e845b09a7217d628f1340c453cd388b1da17efa05fa276f6e097eafa5a3b74f99cd2e114f6355af8024d3b8ddd9b99d73f8c889ca9c64b8e0ed9d77a69f99fea56a660d686198b2405606c4bcd45e6e727b19beeb31c8b47b0022f7e47c9d8ece2b6f2ec0a6dcb76b443a39b13338920905390616eace86842070b5c9426e93e324e7272ea108f073748438c0af8d393b38edf9d52d21fa46766722c9289692be58062a5ef5ff8e5849bb343ba9a6266e53b7153b1bce64d139cebc408ab095f5757025b74f6c065b749ccf62bc64a21cca2a22d79cbfc59c513e29976922d085470a01274a09f67c7da32d829b46f8a042a53d3eb56350003bb9c5a13400fbd579bb112e3a0b477808a9d8cd63e3e34a09a6c2af7d443f0fbe7e1a3d28602877c5761867c1c0abc4931171e3bac1608f5cbdc9381d0963de0bd82571511d3ddd658e2a9728021432e7cb66eeda6d4fe87b4675e176d187b219dd77ade78b86318f461d20abce28731e07ddcba90a7701997eca0991f165923a36456ef637fd79bbeb05adb1bc23f502c3bfeee6605b6d3138409c87bbc364179ec25da46d7ba11fc92caf8b9a23397d2e46e9a1b86a90c7c99d613f1e3c085c6b83809ace5f1d6d532fd1c522723decda9bec99793be2dc9684a48fb3567cf2605419f85d8f009dd728169b519f260b527b25a970daeeec072cc98fdc23d4d7730578cc6174d7f8c40619800139210dc43a7639768d87a358423cd79442ad129bdb6adf00d0b219b20f9baa3f2d6ea3fe67d1adefef449ee22a10f904f3bc7abacd99005e61c338f80406fe2c1933d5ef2bfe1ee8ed9b1427951d5ce603bee9834b4ccb297e2acb20259a5cf41a686e3587f4f895b59eb0d527a1f95735f7bb29bb9d59c94a48ee403026ee06550afc53fd9fb85a8410a74a7fc03ff4df74982ac8cd2a8d5d1e7c0315f9438d3e31c0e36cb921fafe6e806f6728654181e51db4cfe13f9d36342bc806e818863c7e1e5efc1c5168be37f22fc77ee04a6589a85175f77bbc9c283f332393aa9c6b3dbdef56323f063b8fd6b835f921f5dc403f4722f0829474431ac012c288f568c10edfd731dd5597e1538b90cc7b7ff1fe00b720d0cb5a021ddc470bb7842a2fa779ed68d676dba9ea7a0736f469b4b0e6d232fda6ac2e910168b5293b84640a7cbf59198c750df56fd995b3eaded30053ed3c0154f1785e4cacde1af52eb9d77775b1d329ede55e6ac3c444790ed3cef6b0716efe2114906cc0622618ad8668e123de4801b43702661449265d930b426e51a24f18135a1e5258d429b2a9858a26bb3983fcec97e10769ff4b430d6cdcd35b12d5802199d5ec720727dd5bad0298f5a7ea86723e3a66074c24fb52baa95a92322d9b664f99464c1c104e0de8fe760ddc5b596a42802c6d711e028d4bf57fe361effee928e831f964157caa124877aa387111135ff422bf789f61ae66a3fbe10bb39d5d0d1383320205795fc08bc6f17224289bedb10920ecceabd9f94f3eada029e345836ec25f0d01603105878447d2d67597e5863401eed65d49a187f4aa2fcb510bea0c4cd5bb2e8cc9181a570eec275b55e8b03f856d06395953c5c0bdd93c918738f629e3b90372621f2309f9d913fb456f0c8d20b0d7906f0bf59a8d8e2edd27b7d08be9fd26ef9327e0607d7f1d49c2ae3c4ed6bbf50c656577fd1fc7078821ab2a5324b40de3d2b4e84e33e7978b9ee0aa7495eea94da74589fe1148d56c03f20ea3f2c71329a770903472464d7014f35b3107d1a7d0c8c712265fed9e04431af9683ed5033ffbb3cb1af11674d42845284118d87134424e057506f42cbdd50c9f8ea0fb2936d4297ba69dc7a4353625866a89e1288a75b11af9957d992b2a4e8e11846a25b4e1685f8c89b6e18e05192aad92ff0f8b7959650b7ef7c88435213bfac0c41b3610ca52adfba126d2993368ee3ba12771f382a8db73989bb89b8f0a2afd4e190eb1950b78049808b2cf3bbe30a7a577027e638b64c4a24c610220774cb83924db815cb811bc9d986c4e8635112d135517580b0b3a489184f1d6317697c1f02d5097e64657cf18e301454bf94c1f9fabba3bf2dcc67ca5e1cd0d84c7ce935e1a7bf1fd835d3f46c115d3500b3a71f60bdd42d8aa66e1c43ac536a5f3095e942cfa2e99494fdeb5b586811c476ed6e5b2cd4205c149725592f00c043a7eab8924ec7ef41f85d65c9c07c46f4e95ffa683d2ad865fb6a1da4be35e301d0aef812ca0e15d566bd5366fcd105dd32b7c8abcafa8cb6ad02c158664c34c8cdea2cb2ca0d2b718149bacc1cc8ea8a14a6dacaf52f5e8797659d8f2f4e7028910071f10c993da8089e7ab97984622bb7386c1d4bc50b386e32a10616402dc89ad01dc1cb758476fcdd35cd2fb578af3ca05604cbd47db4d07750d818b4dcbd2505d6d11cae381f1b23ec2f40da6277b5d0b6b7f4c31c839f0b9fae4e29fdcd958170528b072d20601928376357f3b43851eabfe95a972d6176ed7f96f5e721f23ea7a07ed33dbc77fa93a56eaaac2753e601ec8c951b1562f1c31998301b03a387cec2eca7ff9ba994eb3a57d8a589ce7e237bfffc7681ca1fa3dcfc559c80152bb1c0b7d8ee21748ca807f20c6fbdf72c3a66ea6a830a5928eb049852c2460e5f7502dad794cbb2b673ebe853c482df5aa18c14e9e674451cd881f7e83c219fc8612073d1cd815ba18d7aec4d84a3ff2bcd2522b3c799b336602c4bbbe22cc9d489213a88661e6638d7d3cb1cc7202b66a7ff096ca0384610e757215062f5a8463480090ddf6487c809e131b04d314f631f7b624021e3affea8775d156bd7ed296fa", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000005dcc024becbeff142d393c7e24ebb4140000000000000000000000000000000022e67462ecbabe72cbabd2ff92be537a11521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + }, + "decryption_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000cb72a26f4bc2b655f000000000000000000000000000000000000000000000009d27836abb5c49cea00000000000000000000000000000000000000000000000ce96c2d18739d82800000000000000000000000000000000000000000000000000000d5f41f8cd725000000000000000000000000000000000000000000000008883f6e266d0f541b000000000000000000000000000000000000000000000008c334df8c530f7d2e000000000000000000000000000000000000000000000006f020d9ffd1efe48b000000000000000000000000000000000000000000000000000085100738e49500000000000000000000000000000000000000000000000191dde7354124a1a4000000000000000000000000000000000000000000000000f51afb5d050a083700000000000000000000000000000000000000000000000cbf6b9bd0271d19050000000000000000000000000000000000000000000000000001f87fba51408f00000000000000000000000000000000000000000000000507d6c41d8c3244190000000000000000000000000000000000000000000000025dd8654e5a64657900000000000000000000000000000000000000000000000bb69b4488a6661578000000000000000000000000000000000000000000000000000000586bb4925a2313c38a4107f7a1bc1cf105ff5dbe5cce29c167c6d1454b2ec0371ba20730be1215aabf311de0fd5a7e01718e75cac3a5f3d2e9345bee5f0aff320d0cf441b22b940278a9e081c76995173a5668ad1b3c21d52e6515fc1c6111bc8570a97d2513cff4f84ee3292426574cbbd61b9fae99e95fa54d61edd498a00035ccbc19f911aad47df27a1f8d80c091ee4f4722c1831b60a2ed67b54f0d892a3abe9cf9d6000df18f0126f35f6af728bb8c9b5d5b06f5f0facd0ad007c578aed54dd6350d108ccd74a2a74a956663b2f5295ec9f3b56907a436c363da245fe137e4a27ade2d50ba826d38743ebfb8fb237263a20e1cf000ef5a59ebcab9aa9607b8c53dab08008b33f585fe76174121a00c186674a178454a2f1161fe1ce167675ebfb1ff20a7bdb97e3b74ae55d582dcb0becf8e0252d35b442e1ecd9ee727d3c73b1890189f5984477376e807d8fb5294234a8b233a26f9b6c718cf12664dbd17a94f2430404927f8413c9c4d3ba95ebefc4df717868f29e2ea8eed471daa1b632670401475056ba0bd6c349b55e561f7255f437cc48c7a9f7778ecced8c8d58f146f9e0b05ea0d166787c39d8d0bc4db81911371839929baab4dae64cc4d8d574fd30c1d6ba7d0933eaeeaab5dff6cb1af30ad4ba340edabb6dfe01e6f3ade14e206fa12392c605ab00e35de0f3bf35a27774dcd013d5d4038643b21508bb87a59519c1cf7aa122725cf720db8e870728206d50dc85ea0ef6231c890d5c943ef06ebd707e8c9e2a413e944d576a78cc3886020b1bffc604797f597712e1c8d81e3ec6f2542d46bab3e1415b01a176f2cdeb38564ef8a903260938cccedc9f22d3984d92b6585980a340a32614b1ca661901e3801bcfd86a4d1e12191b45fd9d216cb851c7a4a7a32501751a1a4c066f9ed267238c015616330809488b9028ac1041afc06eaaa51442c82a333c6690d7176e596a4f84454367ac1eef73fc39a250e9aaf281aaf24528af51d7061ce7f33dc128198c8d69c5512195e6ff5c7d16e9d25c00bf088a1174233241186c1c93e4544ed8feb5c12ea7eb189f8057720e47524ca15bb6834930409a27b99b6260b9d5df8198ff26dee77f148e06d93bb26b5da1110a5b032d732ac9dd68ac656db90510eb6784c09b5a36d3b653e4ee61f4f4fda273034a8a8a9d894099e482217ed8b5866a4a1ff4beb500a8121f2e0e97c28bb191fc3b85b16dd65970de19f9dc158860f2fdedef692a5801889143ecdd701db172d0437ff30d393a70585b5296b85bbeca15d9a670eab6a0dfb4811882d1983158c4ac2c1bfa3ca560948e794018e4f76b1b2bd25478552837685762e3192112658e9a1e375972488108f6e4ea30062534dc4eba65fd538a3913a9b502f37f411adc3f57187ae6922cf19790a0abe806939d842a4faeedabeabf8ae695fa38b0414ff61f933081d600479b086d53a04eb9332d8640b27783be4808412de388a16becbc94f095ae4f213c20af9322cd1b0038368a1e96bd4adec2338f3fef17f14637b8511223255c5ff15bd5cccc22bdfc3f2d434d6d5037a97869cf0dc02b523019b5dd4f9cd45f07f725dd1d8b0fe2fda8358066a603dd80599f3436763910386264fb6a42c239e760975d435ae996764e3a2c59d7d59c69c87b70c2a295107cea1f16f6c9488b10bc111d9ba5ce00dd862e42d4e06c6f7157fc9f267a337154ae8956a5a22d5ab4ed3d1a57de54132903b2d5499117f60ae26e7a82c1117099713785a7196ff1cccf53bcaddcb8df2b758b68b70bf9aaec914925fd1efde0287acbba337ec0e101459bb740b78cba08a7cb9467a52d94ae55e68b4d1eae304215101d9400a36c7d0caeb436d7eb4b929fee6394c10ed15f589466d2a9f682f6a1978b75fcd7440fa4b75a9ece24dc94877ded7b76f596687d10a4af92f301c3391a9d9ac3bad220771549d0b32dcb0c21f4e9816437fa46339dbc0c667ce03a1456de29a3e69252e7609eebb4743deb6fbc3ac6a7226b5eb7ab4f35f5e8a253e7fccb26dc26f35d4d0897f9f34a98a833956a5d8a34f552cc52b8eedfdbe2c8bed7dcc889be5ab58f16a066ff0738e677b964dee8f77d71ca6fbdf7f6a5f1efec00e95fa2cdf9cb13fe409c243c73bbfda0da9389698bf926bf678cf08bc1a154a4e2740d06e239be0ab6037453f90080c7638fb3a4b47be173fa5fb5dd927386e66d7c301a0b7b84a433b7abbc0c17ee52a4610ac27caac41f9a57fd6572f94f3f0feeb716798e345e74c026fe1eab060a9753bcc6b494adae78b8c48831c4b3068e874e8ae97f098c279efce9fe97431a88ed6383e376b697a8dd7b29a06cd19dbbb19bc26ef7f8e24dcc533e1e6df210f3d9201d0818a2acfed7cba4609dd1f04bba079b62438571dfa4bcd5f9cb3ed792201d81ea8b469de00d554952a10164c6602ba7ee3c93eec521d5b37fe71f00f135dce445d919d1456416e0008f9e1ea2c5f0edf3d8d0e705fd77daed72d4060376d93aff9343976c9f9a9ec1c6fc299607e32dfce088440c1b27910c04f99a8c19bc048504ab3ea1c3149cc189d32066676f85fd4e4b58360c14dce07bcfefb8786990fa3d2d02f55df9d291e358eacc2f8cb961241f3f1aea3f16c859f9a9427cacf73be9fd75855899aa52effdd8445043da2bd17ae2097c5193a37aabf27215edd430732a1968ba5c5cd163fd4787fb223c6425fe79a53a468f901eca21952d954b4f1acfd51fafdf82727e491c3bb70e64d50280395515573b29cda31e01e06f7b2e44caf0b74733a091dc63384e63175842a450d156987bfc733bb50df42f4588a1d8aba142ca728b80e2c5cc3248d531aaac9ab22134aadc1d4b8235879649e448af0d13256e8c70d104cd193eb25bc3b69749015b1977e911bd0360598ead3e73053fcdb62119086244fc8f8198fa0a02425d387d9e842b0f98a41710ab2cf859d59f93acf99b00e0ec230d1ab4cd5571f19f9a9d89f04a9fbe3eb93643915d3083afd68d6ac67e51a886c2e7401e737f324852eb1058647c1ad920bcd4ce15f4a8f1e05536e690c1cdd151b9c5c3bc9eee54d4f92e069b280834727aa57425fd74dd464e11d6e47133a5dd5da94b805eb2de7bb269a9ca87124d0ed304ee1865a1d7788fd4e0a0111fac6499e357b1c303671d1bcb274e49850271998e5c3fe2505a32320745c9f0788d50f5a3f0f7af6a8a11623e0d90e96261a23a0e0be4406fb1df38bba0cf1126c091c192f49185fc1b85af39cc747a605a758a43ed4de6bc969a8e4b94b4e04103b6c7e771115bc25ffb588df0e378b20828b626f60d91609ba557aa16c7a2a86d96745a4af60cef3377e54f1f0a918418915c5b0843a9e8e62defa23fcc7236cbdf03146f9d5cff3c2738cc19c712bfc1e97d04c592abd2a503977876305156635927f604f6d49cab3351b6bdec01b73285797925b9d56231a26b151cda9151bf01dc3bf7843552f07c3c599b32a74b9a1924bae7e2462538fb3d11f872b301e2e979c50766eeb68e2fef80423c0aa0be3237b280ad3b287a0dcc6ec1307118d89e95c499e5375b430f6834447ebc4c975181d5c801730bf232099ebe89d2361ba955cbe174d15ac109b458838c87e0057fee651ab45699245dffe0e0947125172de2d5a309139f81385a6de0665004aaa2a099b3ba491262ff5446cfe2c002136e84f99687d4c2dd3401a0f272c80729f6a49124e5256dce015cdad858b0fe213385b7f84bb2b36ce9195f8698fcb1e5eca0df20b38066799ebac0db9151540a4735f7dbd647255af0ba418bbedf05cf04ce6ffea4263693b6f9929cfaf1ca0558cdfe52634a0dc615b3937be0d6929c9203f15d3b16ae394109cb12b450c26cd6fd4594d042c25bac5212fa45710eb1ae7c12035f2756cc5b79cf1275d21fd427ab4df6fb02fd7e07a7ce5912a7ff309c34b6e8214e214933e375a867e17c84b3a9f7402af2621a1737a0b6b35265bf0e27109ace625439cdda98fe2cf0b8cea574da812b5e8675b450ecc2cb603ba8cd9d23856c0157175a52310c26e16ade24aeb764c5c1c8524595eb4f3add96c7b8dc400a42135dc18da82da97540fd1231c528e70b362440faaedd8d9fc6984f9130a767dba76e3c7a6bf758fa40995ca9d7a5af60270bec0e91cc535b007a685c8c839e9c527c5ab8eb7ee448f0086c298961b8c479e1d2fe0bd61eeb6efdc10ec5db741331f4742b28630fc8f2cd7ad5ed08569bddf91f47e40f0ebc70c2e8d7a5344ae40968a7e06e22fdd9c0a269f5c1d73a857aeb0879fb7fa857a73df6fe8c5699a8fc37cd4d9704391e52af11ffd63ca8cce301c4d2e7046f3e352f3a1a84127940296b9bd7677c48e2a09721bf6df1939a2931b67c40dacbd49c2b0b7b73e173d0257867e03c509a3d11311889591d077ac2e47fa0f0934712deea03834c1f20e1d52b360ae53ef69cc2d098bf4d516ddbbf219dda0bde7756c7a969bdb7258490da74441edca6545b61c59a5c01c7bcd3f276f4511de30c3c9c60a98c5fc07844de934975c350267df1782ecdfed3b15fa815b1eb0844b9a36c33d3f4b6eac1d5e829af190f37cab9509ffe42d384ec7e245b6140a75b7c17af5d44d09b4073dfccac35a60ea12ecdf055f473428571221c87429f479c89e9dbcb99f63dfb86298d9b0357cec7da6532c0bcce9bf9e5a5fe86f44ac9f07f04b76dff78614438b38939c01718301fc3d2b5de0c98b93441550e8d759eb21d914ca217a29d6be440cc2d2368863129b37033998b03ab2db1ff7f43c41ecb606284c4ed2b966ed1b3d7545dd1110808c25237b91337b27c2baf9f82732cca4fd15674245bb6c13c418463c31fa7718d281060f50d783fc5c32a04b819c7e843d15ccb658e7ed3601c3e11460874102652d2746e2fc3a238955923c713d04ae01e1588718ce72710ee2d4744b2b7c7f066e23f97b1d534f10efc221a8adaf64dc52200370e7b67d2a1b56dd5c2fc3cd806315ded78e4cadb5b5b4b51a2ed1a09deccd8f6d7ab212cce6e667d359a56e1d921d85c36ffdcc4aad0db718e2822ba268be4cd25f748cce52d28094aa304d8ca2003e17a0062957689eb6c3c8d22e491100c9049f0f406122b94a8496256ddab51f0e3e1ed32f21fea7688dfc76e43e7a0a0f569fa50e55ad20ebc7281700826926d2b55248000e0a120f542210ce4c3c666232bc6e2862a3c5baff4e739870c010921e85d0d413b13bb5fd7d074c5b85a3e77d5fcb87c2319f6d059759bae5cf2a913ee4800b575b2d10c30b1e960accfbfa92bd0270ea55fa3076b4b583143c20a8ee6d30c78d807de60607cd2469b66b5df59484f0f751cf0bdb2eeadab0ac0bebb994ee223175e760f62893d7c241aa1e5dbb177114b55fbd5f81d009447621e6b9e212ea7f25810a7ea5cc34eabfd6e35a0d3daf29c62fa09ff67c782a0b0563a6cd009e2ec14f1677dfecfbebeb6e18dd9fe2759bec8c2259c64a96c6e62cf64277d23440e9df48b88e25a23507a0b537621cf6d65a7cfed83b9d5e1bbc030bab18c76b60a07de1cb3fc5d9af66747bb0f4a901a930ad67e3ab13ae17d017615cab0f232437159824ce1b76e9065e9457dcfdfb4ce9b364ed06c959cd531913286b79453bb50e0dc4198993b58d03f61d8f44e5b195fd6a3ac0635484d31e96f84fa9217fa2c8cc6745a13cb027fd85c3dc5c12e21291ea1d4143f377a816c048dbf593d4ee59c1115477cc5fe0921b0751d00b36467f73f31d87809faf1f5ff7387d960fa1e9e7bceeac38a4b3951c7887e57c73f639b60c03c9e2f10e27a87b83e774dd7ab1476413dd75c179f4777e339a431559295099e4a521a0490343c381f3704ac29c32f659803addfaafbb556da2f4e2e4e20c9cf1a42d639b15c324488caff0a612c5d6e23afa5c97f6c08140c2b68b5e26529b7abd3c422c033b8ad5cc4aa0849611f1336a1132860abf0496caff22ab0d6d6a8571362cd21cf10df66a695c3a3fae7f5c4cf0b3cd05312075ef875d210ed6618b54cc70852cc8a2ce2934aa228623c7d3e28c8b95a3b639909a68786e5e2cdbe9d83d670602f57f03140395962b50608e083702fd9d54fb26cba708aa32303ec6db94f08929ba7e70015e2dec89d790e53adc8832dad12bea71a76b062d9af4914a0712191517d6c1f295db9ef064765e3bbf16fbe762134852bf0d388ce24bd179f721c4159bbef83fe9a1a2faad7860284fea07825e7744c103fdd244164abc6ebc3d8a1a81dde0301fb25d53907ebb44fea80073371748b3cba4a5146b7e26bb7df99419b6c729bab92e54c5b85b9109c89bb8117d10473fde6e238cf3be539441fd7f265f5c1b07c59a0af8eba52e61614478d50b01fa5ae7b04aaa8593c2b4e3f8f50c2e2864be975e2a0aad0c687128530a4e7f729012edba9b8d5db3ae708aecea069440ee37908c90d60d45203cddecc3b1c760c4f8694a2b31be3b69f6fb5bc42447dccc556c4f4f3dbfb61d9d00203ce2ee1b2d525a12ac890edaedd7eb5156055b878d91521a378052cc88eaf6ff10a20578c029bbfea6e1d7d4b8ea0b70bb3049cdf4083e12f88d74eb75c55488b3fadd884d9fbe62ae22a81fd8dbe4722a27da0e76c31d588779327ce844c20e507f2f516133ec25e6a3c2bfebce4d77f718f53a59118d337ad80ac883b2ff1e0879503dd15e78735fa17e15782e3f05791e53855a12f699b26092b96a0a30eb5157dbf3b5707a16f397dafbcff4e8ee3102d0cbe81199bcfaaf6b3ab751aa32045a578a9ce9c2c312b1847a7871304d4710e5eab9d7ccb6d83f7754cb4e7ac2efb44d45d4165df31c86d03a17a375492a2078373808c9babfe4210762dff443c65be414a814311ac3734b60be87a6d5f50ad1dee8f7d7ee2945cec9db4abe572ad1376925a930daa6798d27f7d35f5f77133dee4a69e0096c7e746499f3f3fd1077c29af316af120acbe1513d97992fb42a9c4464ca87b2d8f9c104a69f28b8415268fc2e828e2824f026f03af66fd1e11f44b76a5c49ae396cc5d3203ca6abad8f756fb8ea36c8f35ea527f382f35eda217e8f122cc3061ab2a2fbe180883059747fa8e1784c0b515eba37bcd6d4a0b51ff843b8d53948501be8a3ac98c02bd9fc903320b49d12e725916c5283f9401b27aca219a88cd6af41323a73b10eabad4274edb5e649ef93d3feb4cbcc5efb28166b07b8e7dcff384040f8c59e62c37e59a90dc87ff0f7610418db2e75e7d7a82b84a028ff05076eec82ea0f4e7fad6aeee05c0638923ee6352c22bb426db70b075b23591b07cf49fe34eb90b8a4b806d9cc9af3fcf24ff60a2d4ce065960bc12d55ca8b11e60998621555ff79251140cc84f39f339c102f774696e94349e63e07bb5d0e9d8a10f5f983deddbbf018cef93fcac66850d1bb2d378a859993e43d135dc83a4f66ffea12b85fa5b8df46beecc582a2e406b09252da9fbd01d6208501523ae368c7179d70c238ed89f951dd6d7a10253da90e68b225eb35fac658de0b7b96f652ff291cbe7d64daa91d40c9dcd19ec109d592624caa4358483c174027b78b2f933df2f6b098d71d2ac1d6aebc2fb29361448d44ebd93e3468bf22110e8aa311aa8db85e773c3067585caa497802475fbf34b1f148388b265276abb5207492d086f609659abb12d4d48f3a2c84d9c80b069025e36e593093fb5ebeaa1725c6a5df89f634e9ce26ff556cf6f88d7b67c7e193055ccacc20c9eb78ab721af3ea516e38ae26ffe5a7b5c1954f4590a4d52578cf9153a9acf33af550c04926911c6dd114937ab8ea8147d08c7db5716275245fb951c767f82430ccec444501d3c16986d95134976e7a7203583bcde83af9a99aa003fb4b55fa03f94c09e82329669086fa0b5fc56daa0ad6df98574c612c0788fba15f4e3fa1d3c00ea3ad1a32b7c183d6a097f0413f606f106a6cdc0e93a7ee18830496f8e1731626428f01bbfae67ee1909bc7d84ffe61070be9ccfc109b5e7fbbbd27452a431187c53308ca03e55d4845dbbb575edf975784b8242889b19cf9001075a3a0d084bc2daf1be59a8c0addb7c6fcf3d922c7d19ff3981f5c9645b031839de80b56da2982f426ce8869036679da3970739f24bdfdea24526fdabaedc36040f2902b1b1119e72c14c74f69aeadd07892831a5da635c6a312cc1139f8ff1531db999dc979200100719683749f6cf50a21868958e37ff4786e7b5ee1703d43e9261f52d12d59582986a895846ad4d5c7ef17aeedc053e72783f575d909aa2f782938ce8246a11e1372c04b95f0c9a1fd6ee50f1121f93cb391c50d0c958a37a68add49cd5e556b25a982c3a0b02603edc7a09e993aa34d7d780e71e955a6c56d5b8a16209a386e2e989ee588189f093e06ef28f0cb18aac37a1b3c369a80c40b64fdcde957ce4f2ac250b30125a6bb044d6cbeec900598bbab4ca89612b3d2c264a28f034873672e18d7f11e234506bf1f2309b7444f8db2c7d5f5fe70c612fb85ea9dbdff796807ca6ec9c93676363ab70041ea6a529224ce93f1a0abc53a17701c0c3d2f4f6d2ede4b9fb18914a4113f65e3c0c6111267c4f6be1fb8046ef25bf35c44d19b842ae2fe2b889e477c54c98d82da4c7b0612603d9f6e561a05948ab0c30e494e0b2635f26b324ee6ac274f83f257657109d07720342b7b7b5fa7637f477d41af6c08a23725165ed2a19148c9ea0ba8ab684b496108cf65e5f4e7f0ddf795c404120b518f3e1511ea4f7546ebb2b1caab95ca3f24ea068450b7a0099859ae7c1ac908b13c49f09e88fc97a883216872bd6858ebc1fdfe5f52dbd0356e05257d80320fa187705c51f66366f2f05466b1ca3c892961f9a589a6cb44bb273dd4fb84551bff7f3406bfa4f63c668260021425f9b7163670447b8235423b3710b5e484bf3039a7737aceb27e9be16933623f1cc9c9082c83599759847de30c04678606c6003d492575ac04ffba536c55e0b25a03951405779fcb7d3cfd98fbf3df1e61742d76e67149ddca631eb12e528d988b718e63cc9619433b95482a23ef3db9d01a2005fe40cb5a62f8be9698ce69cd4b9db4778bf5febbdde68eb90f19868969d802ae7a7d22b8d6032085d6040f6c03b6d7492886d6301c9afabe287c6c45b0292b310e8d9d7449443006fa8131a2f3f5656802d209ae64072361a13cd7b0f2281fab02ef911d09729456a937561f28b22445560977a07b0e00888b90f5f476c92db9d5073658a3be4ce0ac5d1db0fe45be0b1c3bd53829f62ed81d5ff6d51ad31621458d9822ae610b033350f9c993233d79ae279ea4264e67f31efb46dc276410248f27d94a51f4c7a665bb24cc03872c8cfad6c883481b414686d970691abe0fe37c44c605610848a3076689513895acaa05834a3b9e0e024b345cd8e1b87003d5d7475d881900fc5586054fa9234cfb46bea65c71eca250a63844c749279f2b5b49120d771d71fc3500f1d125662bc80e13db85e6695c40b5e7fd94ba3f2122663e719d61c8829a3f55141ca58c5ed1e28beeb46bae86567d9b7808fcc839235222a95ecbe861aab8e7d0c4e600144af761a5a540fbd9def7aa41cfee261e021e3286ab9b59f7a311d01c19931e346a5bb1e1c4d7c9bb3faec5f1ce5d2643047b3bfc0e3659c2b8b8f7fc07dd843d506d60a8e65056c2aeb3cfbcc25149661e2c5eb1eda809e387a860c1e9a7f37e1ea8a18b48cfb239aceee1641fd9ecc023c4925fa502947194eabc25b3a8948861d26414e21f19e7d57d23ba4d935a281313be2a19396e46e733a2d08840076c2f9d38c7891201640355969f72e9492e01f41e5ce954612273f29ffe616557a9c01bc2ed58536ed6e0171a7a9631df70079f38ef26f2bd6a9d0fe7e253fdd1fe098e9e59c0fdf14400cb83091242267f205c26520404b3df973224a3564faef86238ab4e08c67bb4cf64f57e17b0ea4b1cff7bfc129663c0c261d026fd7aeeb7fb358b296c7202c8b2eaa839b4f9175002835b9ab3ec3ce576da4ad7ad83dea97c3d9ed7bd366878cfe8a5e5aa28c0df0139e89362425d85b45523b95a262b5a68718df8f13c6fbd052ef97bdaea84ac158db8e784574a71e7211a48e7ef64c569161cd19539ea3d1597e05b0a83356d0e4055519520120d0ffe93ec75da6b630acfed959e69e25d46501659373bceed1306c3e92851ec6cb8dc53f1aa72aa8bdb0d85a815d3968a9b034ccb292a66b310935d44098cf4b92a29ac0617d8dc6322e4ffd1afeb5813197719186dc6e0d600dad3781e520792127fb8fd72c9702411c0a1f947aa32f86510936893607fbf0de068f52c56a8691c5d4c02741954d82563a90dbdbde6a4e4c81fb7d620ca76265fd84201581590686edc35cd730bdecd186812b216770d2b3b15dc8a398be425649dc6e4f3635a2b9f8552be41509240dba79b4f056366063fa0f35bafe42b110ca07b7f2c8034386bb0dc771415aa8cd885bc06160e77bf85f5c3655d641624fcb342117f94e4a5afcee5b98a648baac82063d897ce4a82a3feca3bd6ef6b2b32f06f9f38eaa15fbd3f49e6f411ded6558ae35c4911ab1c316db2b01ddcdd206ef277976083b82e474813b23b4b8d9780e0bb6db3860a269b5215d33878c207c796c7a764bd2bf1bf255249cead62b9d7b90051ac8375a1701a271a5a504b025d4982dc7781c4a68b774c5e14d7c7a6982229b6a5db2a69012f9dba9a444e178223d54934f655c362925278725824ab9bfc428d2026d7725f63f6b210d08103b372038eb05ad0deb2cddf7b9ca8871fdfdaeecaa6e78480148cd53d772e002fbf0df391012015bf65477a51e31a284c0dd71fa342e2922818119bc930a61500f7c8b0f04920ba24b379b6cc2952fb3119a8b363893def2a9ac3fa5bb5a36e19f5e706f9197907da882af06c4935b07323af3293c75d8ddcace5e0e5a5aebb0717fc5308f336c38ecd96c89c9f7422b335b0a4e18f88656f255e9459d477e515a56275db5ba929662aed1b09c814b5a3ed4920039bd65300cf52c70fb7a42a1c81454eac1c3d276f1c272f81a95250fadba17c9aca2c0583aa2bf5b0d2873418a9688b46dc7ab1f36aeb09ea1b00873a8ff04e4bc608d9e35b53e7735fd5a01dbd1eb40cc29b13bb21e85f00fca65fd0983459d458f25a3bdbb09b4094ba692896c82fea2e9353d9bda7feedf2416f1507b3342c0d3d2a2a50809022c001bd2e6412debc02a94d461ece20fdd765c74e78f6e9e43fd14d935b7884b716a0692004490d547b1c97f3b60288eaa7daec61698fc7e0d302ef97ad1cf8cee533b816f6f722c3056c097c802609d9c7b074c50e75ff09c190d2cf732c974af4097623dcb10ab54bfbe197b4e971c40d664e273b6dd3e93a40a8ae5187ef842159940dad07983ae2f8e04736680fa307ab50921708d716bca4297a7ff0c816b6446601987090bf0b69c6415de518d73a56474ebd658f8d1e9aa500e0020a6c3151201b11a3cce79a98ad901722371fb8fe162f465e6319fcf30209128c969701bcb70001e8aa66cd3c38449bb2e520f07b9e1fc2fe710ffbd456d39711efa0832ace2aa3c12f7793c3b97e69d5e28f84d9dde4cfc2990b64e13022e5472b64c29c782e4cbe5186a9cd9f5c372f2a414838dcebfa655361de8f64b3155ecca983aad81b51c65aefafea18e7d0a0430bd86f301462df63650497152a83d5518eb590ad03c3b822cbde2466c3571d685b8141f972d64a770a52f2912ccfa1af908e84570a58db6f335e36a93b8b46c8fee6a65d1dd1f84aa7d0b022392329cd47f1acd8149af8bbb40ba250d374000d409fb81cc9aaf8c8124303485900e653f153c5d108e2fbe580e582df090d041a64c9631d114bd77943a103f09961f818567c3c711c3a5b6daf0e32e910da373b362b9641706892cb96419e5a8c76f29f34c05c4013123379388bdb0e5883b299d8469542bdb58f62773c942c5707f5c903f88c5e056779d741068745343dcb01bbede6be417932bb0cc0fd1c86270af2cb63496e175cd36612fb28f4a8affc5dfec8a844daf2e67e83ad7db40aab6dcfc23413a702336cbaf76a18fef3c7bfe65a85cf8e99d59537b8afdab62cd6882d17bd1cab00c6fec1e87fb717719c8f1f73751ce868eb93e57f813648d813dd68f03f30521c2976a50f6721e68754e5fc7369621b74dc6c2b3a407065b35b5dabef052d1a01ad1cd893425a4d56671a57219b26d215fc4bdd5b53680c43453081304b374507a566e2c9d5827d3fc804809d7a8aefaafec10fccd87763c298c59dfcc7a69318dfd77590a160094d3a359e73ca0c1279cd5fd655a128f580b7fc2758ec480317deb9f9addff0b92367143d9cd8f30c84b15b25eb5f4a47ec56caaef58a6ff1139792c89b163437743a4d60fbd172e18b270d653927725b28ec3f69a8eaece000f3d850d877a132fd182989ac44e2aa29459f7b3914f4f6030076ea47ae401b203e48b45fa72c752b9947086a35a67b68e8bc183f3e9e37397cfd45312ad31908ede3e9e4d8c78e9e224e1a42c56f5bd1980e06e9b1d557ba4d57493cebaf89100ac163e836ee4f1816600283dba01f6b1f1699f0c1ae32630a712b828fc8fc16442afe40c2f51e6e9ab05fc7190e8bb2415ed1e5a42ce7e4c858005c353d910efc9eb2cd154c9d5b03da687d58cad45ddcd6ccbb36062282ee94870bfee5031854ba4f8c71350dbb06dfb2fe67ea4bc8853f2951f36f89ca530c4ecbb2a3b30869e7f23b5b62505613dbefde6e5a8670649048987fd3f588e459fcb44f65c91324c04be47f7bf00f94bdbd730a95c86122be5086d212e10042e980a2571cb7164875082c4e04eabce1d7df6b6b74149cf0d6f5214893750882693d6837e6cb2c846f42cf78fc0e634cd304dd118bff8386c3616be40b1453caee5b2e833a4817911ac39961f385178c49973efbb97a5ac5fdb03cb61084c3e58ca4284bd78510e102da33ffc70684ad4f19bf2bf727b1e19f421915eb4574442db4a356cdb31b5a6ca64ba0b4bf26344a21d31b62575d12809c18f9afce06a7e61ee6adf8e205a6d7e18315546fde9232a5ba7f09a05f98a64e90480e069a3dc1e788465d0211443339feb7ae07069444094267bbaeb0886a7395a2c858b74bc77e9c12082916cf2bac5255804cb043cae050335971a55b97ded520eba50eed6389524dc9cf1cf873a57c6bb3c9eb90393a184f19700736c2a1bbee9d61bcc848e5c743387b058d210bff2531aecae2e31175afea3ecd122a5f7a43ab8c7ef95fc562d5133604e5a81e8dd44d7dde63dca73a34cdce179691cd99dba061ba1e52664d6e8b531317f9cc29b4e6737ff28a14c91ae16a9509df249d4caa5d7a9accf203ce43ab0d4a819495cabbaaadbb284b01061f61add5ec9c0a9d39f792581cc1201ff41918dc8f1749902f8d4eaef9bb1385f4d84cd68f9e86b0adb4c1b961f3a4fae3342fe7b21f2b28fa20671100694f661fef4ca029f391f9ca4caf3cc19e39a4981f131d4f538c71f968fc0d9f03cf323fb1cd48dd9bb900876c300a022f3e817a62245004d95c4a1b15d78fc5024996506557dd9ac3bcdaab64e61559e8ccfaf4480c2bc8a49d41504a25a33ec9f6e81b37e9550f8df92d1d49f3df4724c28ba71c1ae21a17e393aedc796558a02c5d671d715aa45e42efba7af0bd351bd712f9cb21000f926d7c0f172c62aba013fb20716bc976182953d9378a505980b02d55470a9faaf9bcc8290bd4b1d1f3bfc1e7a3db16f44bb060e693037eb8c2630853782811e636bb7b14aec5a9dd3e96b4f781cb0827aab48e5b6c3e3c8fb903a57f291e35a11477edd96f428ef8334e311a837592ef308cdf95e6b25bcd99eb82b7470af7f98f8f070045cc5baad5dac76c953ac04bd81c2f3556964e3779a6d49bd125b669ca6ff09c379f03bb30cc3850e652ccc14af965f1e6f7304ec6c17f0b8f032f2223cf5ab06bf1fbb19b635064e577862391c64ba9c3ac9869bf77b04ecb2f304149ad0dfcf55e5150dd5e739c170e146e0b68e778880c556fab86fdf98c0b4a8ba767b05fc1b31ddc271538d4d74269697a278d281e032a953cfb62016213166fb3698dc2527a7c37dd6350366036a9bbe0aa7e36349e83e56e21438dfb204239e67fbeac731d256fe906462573ef805387cd62d66593cb274dc1329edd1be34024d2c8c7a529346672c40c60cf1014b3b41389d8401788bbd8cb2105472d79f63e18e169babf3faf554f155eef0895ff746fb7bc96e4e06b0c283d291f024e9d2a47dd1fc370e2262a15b81c5c5a8744919f50bafdb4e8c51df6a934a120eda77dce60c20ecf47300852ba0eae62c5bdd58c292dae1dbe15bb8b7336160806a01ee0096f1183ddfa1847f17e3efa20786a469ffe58a6084946b2881efe1041ffe6e5a395efb68fa39ce73758041c29dd0f2a8c50501c97e761164a1de90626d0a72ca8083f1f970d8944cd3f269c919f52f510287b5faa74350d382f252031db8526e96fdfd680e5a32fb0737a7dc5326b6a1f3c69583113719b4d8a5021ad6adc313fe5fdfd4b0a3d38d789ae51ce92b83939f8386978617f43ad22380a1c75f0fafb5b46d62c89adb165184ce88b27f9e572b9c763fbc2cebd5124e702b2047e10a8735aceb69559dcedaf4a248d938728d651a01bf76c3e8ff7572f", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000002996b0c00e4dc6ae412f0b46d68dffcf000000000000000000000000000000007758cb4d71d9cf4b6349037b19fd145101cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } + }, "test_exit_code": { "crisp": 0, "folded_export": 0, diff --git a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr index e354a0764..67f6a513b 100644 --- a/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr +++ b/circuits/bin/recursive_aggregation/decryption_aggregator/src/main.nr @@ -15,7 +15,6 @@ //! supplied in the same order as on-chain `topNodes`; the hash equality pins that order. use bb_proof_verification::{UltraHonkProof, UltraHonkVerificationKey, verify_honk_proof_non_zk}; -use lib::configs::default::H; use lib::configs::default::MAX_MSG_NON_ZERO_COEFFS; use lib::configs::default::N_PARTIES; use lib::configs::default::T; diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 110829355..ad25ac5c1 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,11 +10,7 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -67,13 +63,9 @@ library HonkVerificationKey { 0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b ) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424 - ), - y: uint256( - 0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409 - ) + qLookup: Honk.G1Point({ + x: uint256(0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424), + y: uint256(0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409) }), qArith: Honk.G1Point({ x: uint256( @@ -163,37 +155,21 @@ library HonkVerificationKey { 0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce ) }), - t1: Honk.G1Point({ - x: uint256( - 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c - ), - y: uint256( - 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 - ) + t1: Honk.G1Point({ + x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), + y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) }), - t2: Honk.G1Point({ - x: uint256( - 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 - ), - y: uint256( - 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 - ) + t2: Honk.G1Point({ + x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), + y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) }), - t3: Honk.G1Point({ - x: uint256( - 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f - ), - y: uint256( - 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 - ) + t3: Honk.G1Point({ + x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), + y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) }), - t4: Honk.G1Point({ - x: uint256( - 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 - ), - y: uint256( - 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea - ) + t4: Honk.G1Point({ + x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), + y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) }), id1: Honk.G1Point({ x: uint256( @@ -227,13 +203,9 @@ library HonkVerificationKey { 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -251,31 +223,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +386,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +584,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +618,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +630,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +639,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +655,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +676,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +694,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +705,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +745,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +772,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +789,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +853,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +871,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +930,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +949,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +969,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +980,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +999,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +1044,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1184,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1192,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1206,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1216,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1291,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1316,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1352,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1366,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1388,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1397,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1441,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1490,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1551,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1582,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1603,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1624,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1660,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1682,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1716,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1739,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1786,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1802,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1814,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1834,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1880,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1904,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1933,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1957,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1969,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1979,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +1999,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +2019,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +2043,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +2075,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2104,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2127,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2139,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2172,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2192,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2253,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2267,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2322,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2339,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2361,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2385,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2404,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2421,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2463,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2473,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2484,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2509,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2519,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2533,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 8f8d5f63f..08a988526 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,11 +10,7 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -67,13 +63,9 @@ library HonkVerificationKey { 0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095 ) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea - ), - y: uint256( - 0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1 - ) + qLookup: Honk.G1Point({ + x: uint256(0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea), + y: uint256(0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1) }), qArith: Honk.G1Point({ x: uint256( @@ -163,37 +155,21 @@ library HonkVerificationKey { 0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92 ) }), - t1: Honk.G1Point({ - x: uint256( - 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c - ), - y: uint256( - 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 - ) + t1: Honk.G1Point({ + x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), + y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) }), - t2: Honk.G1Point({ - x: uint256( - 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 - ), - y: uint256( - 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 - ) + t2: Honk.G1Point({ + x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), + y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) }), - t3: Honk.G1Point({ - x: uint256( - 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f - ), - y: uint256( - 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 - ) + t3: Honk.G1Point({ + x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), + y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) }), - t4: Honk.G1Point({ - x: uint256( - 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 - ), - y: uint256( - 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea - ) + t4: Honk.G1Point({ + x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), + y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) }), id1: Honk.G1Point({ x: uint256( @@ -227,13 +203,9 @@ library HonkVerificationKey { 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -251,31 +223,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +386,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +584,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +618,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +630,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +639,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +655,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +676,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +694,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +705,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +745,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +772,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +789,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +853,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +871,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +930,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +949,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +969,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +980,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +999,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +1044,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1184,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1192,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1206,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1216,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1291,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1316,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1352,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1366,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1388,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1397,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1441,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1490,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1551,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1582,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1603,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1624,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1660,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1682,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1716,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1739,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1786,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1802,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1814,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1834,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1880,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1904,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1933,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1957,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1969,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1979,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +1999,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +2019,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +2043,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +2075,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2104,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2127,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2139,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2172,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2192,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2253,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2267,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2322,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2339,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2361,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2385,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2404,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2421,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2463,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2473,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2484,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2509,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2519,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2533,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } From 81fd4b405df730d6610df09157577381d8d726ba Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 17:07:58 +0200 Subject: [PATCH 08/54] update bfv vk binding values --- .../results_insecure/crisp_verify_gas.json | 4 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1415 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1415 ++++++++++++----- 3 files changed, 2040 insertions(+), 794 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 910381bf3..04418a602 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -17,9 +17,9 @@ }, "calldata_gas": { "dkg": { - "proof": 169968, + "proof": 170052, "public_inputs": 6144, - "total": 176112 + "total": 176196 }, "dec": { "proof": 169848, diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index ad25ac5c1..110829355 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,7 +10,11 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -63,9 +67,13 @@ library HonkVerificationKey { 0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b ) }), - qLookup: Honk.G1Point({ - x: uint256(0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424), - y: uint256(0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409) + qLookup: Honk.G1Point({ + x: uint256( + 0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424 + ), + y: uint256( + 0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409 + ) }), qArith: Honk.G1Point({ x: uint256( @@ -155,21 +163,37 @@ library HonkVerificationKey { 0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce ) }), - t1: Honk.G1Point({ - x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), - y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) + t1: Honk.G1Point({ + x: uint256( + 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c + ), + y: uint256( + 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 + ) }), - t2: Honk.G1Point({ - x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), - y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) + t2: Honk.G1Point({ + x: uint256( + 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 + ), + y: uint256( + 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 + ) }), - t3: Honk.G1Point({ - x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), - y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) + t3: Honk.G1Point({ + x: uint256( + 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f + ), + y: uint256( + 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 + ) }), - t4: Honk.G1Point({ - x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), - y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) + t4: Honk.G1Point({ + x: uint256( + 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 + ), + y: uint256( + 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea + ) }), id1: Honk.G1Point({ x: uint256( @@ -203,9 +227,13 @@ library HonkVerificationKey { 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -223,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -386,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -584,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -618,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -630,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -639,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -655,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -676,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -694,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -705,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -745,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -772,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -789,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -853,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -871,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -930,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -949,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -969,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -980,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -999,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1044,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1184,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1192,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1206,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1216,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1291,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1316,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1352,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1366,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1388,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1397,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1441,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1490,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1551,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1582,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1603,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1624,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1660,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1682,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1716,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1739,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1786,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1802,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1814,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1834,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1880,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1904,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1933,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1957,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1969,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1979,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -1999,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2019,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2043,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -2075,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2104,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2127,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2139,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2172,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2192,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2253,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2267,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2322,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2339,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2361,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2385,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2404,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2421,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2463,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2473,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2484,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2509,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2519,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2533,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 08a988526..8f8d5f63f 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,7 +10,11 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), @@ -63,9 +67,13 @@ library HonkVerificationKey { 0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095 ) }), - qLookup: Honk.G1Point({ - x: uint256(0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea), - y: uint256(0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1) + qLookup: Honk.G1Point({ + x: uint256( + 0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea + ), + y: uint256( + 0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1 + ) }), qArith: Honk.G1Point({ x: uint256( @@ -155,21 +163,37 @@ library HonkVerificationKey { 0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92 ) }), - t1: Honk.G1Point({ - x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), - y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) + t1: Honk.G1Point({ + x: uint256( + 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c + ), + y: uint256( + 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 + ) }), - t2: Honk.G1Point({ - x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), - y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) + t2: Honk.G1Point({ + x: uint256( + 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 + ), + y: uint256( + 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 + ) }), - t3: Honk.G1Point({ - x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), - y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) + t3: Honk.G1Point({ + x: uint256( + 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f + ), + y: uint256( + 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 + ) }), - t4: Honk.G1Point({ - x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), - y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) + t4: Honk.G1Point({ + x: uint256( + 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 + ), + y: uint256( + 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea + ) }), id1: Honk.G1Point({ x: uint256( @@ -203,9 +227,13 @@ library HonkVerificationKey { 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), lagrangeLast: Honk.G1Point({ x: uint256( @@ -223,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -386,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -584,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -618,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -630,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -639,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -655,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -676,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -694,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -705,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -745,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -772,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -789,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -853,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -871,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -930,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -949,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -969,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -980,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -999,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1044,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1184,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1192,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1206,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1216,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1291,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1316,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1352,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1366,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1388,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1397,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1441,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1490,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1551,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1582,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1603,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1624,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1660,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1682,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1716,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1739,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1786,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1802,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1814,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1834,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1880,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1904,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1933,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1957,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1969,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1979,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -1999,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2019,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2043,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -2075,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2104,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2127,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2139,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2172,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2192,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2253,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2267,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2322,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2339,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2361,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2385,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2404,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2421,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2463,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2473,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2484,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2509,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2519,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2533,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } From d6bf7db40eaa151d58c4fa4e94f67e42c7c368cd Mon Sep 17 00:00:00 2001 From: 0xjei Date: Tue, 19 May 2026 22:30:18 +0200 Subject: [PATCH 09/54] fix stale after rebase --- .../results_insecure/crisp_verify_gas.json | 6 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 +++++------------ .../bfv/honk/DkgAggregatorVerifier.sol | 1635 +++++------------ .../test/BfvDecryptionVerifier.spec.ts | 60 - 4 files changed, 927 insertions(+), 2409 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 04418a602..d239014d6 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -17,9 +17,9 @@ }, "calldata_gas": { "dkg": { - "proof": 170052, - "public_inputs": 6144, - "total": 176196 + "proof": 170124, + "public_inputs": 6132, + "total": 176256 }, "dec": { "proof": 169848, diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 110829355..d30ad9e01 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,238 +10,122 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), publicInputsSize: uint256(127), - ql: Honk.G1Point({ - x: uint256( - 0x21b909972bfe373b93f74e8d1b23d4f9da2d359033f9bea3bfd0dff4b14583b7 - ), - y: uint256( - 0x00cca3f61b1c83f29f138790d10246931d7ac90c67f0868928a8f87f115bae3f - ) + ql: Honk.G1Point({ + x: uint256(0x21b909972bfe373b93f74e8d1b23d4f9da2d359033f9bea3bfd0dff4b14583b7), + y: uint256(0x00cca3f61b1c83f29f138790d10246931d7ac90c67f0868928a8f87f115bae3f) }), - qr: Honk.G1Point({ - x: uint256( - 0x282acc4814ca5bff02f14c4a58b751ecc70c1c01494610385bff82d9072bf548 - ), - y: uint256( - 0x1d4bd882f21883ba61543964458224dbd4a53595d002e18c0db812390c45416c - ) + qr: Honk.G1Point({ + x: uint256(0x282acc4814ca5bff02f14c4a58b751ecc70c1c01494610385bff82d9072bf548), + y: uint256(0x1d4bd882f21883ba61543964458224dbd4a53595d002e18c0db812390c45416c) }), - qo: Honk.G1Point({ - x: uint256( - 0x1dd7761ff2c72c1af984fb53d1b3b42460c3323214a2d7dde42c62973d34198e - ), - y: uint256( - 0x13040272a4c0bedcfdec2b313f9efe3ee9a0bbd5ca58d9432dc8d832127e99b0 - ) + qo: Honk.G1Point({ + x: uint256(0x1dd7761ff2c72c1af984fb53d1b3b42460c3323214a2d7dde42c62973d34198e), + y: uint256(0x13040272a4c0bedcfdec2b313f9efe3ee9a0bbd5ca58d9432dc8d832127e99b0) }), - q4: Honk.G1Point({ - x: uint256( - 0x174f2955dfa54bf68658a287d4658af6994ecd888993452c4c14f1c60871ce56 - ), - y: uint256( - 0x0cda4ce133f1492a2f13a0e29c6df44b8e697f84e481f1b2dbf2d8d741be5e04 - ) + q4: Honk.G1Point({ + x: uint256(0x174f2955dfa54bf68658a287d4658af6994ecd888993452c4c14f1c60871ce56), + y: uint256(0x0cda4ce133f1492a2f13a0e29c6df44b8e697f84e481f1b2dbf2d8d741be5e04) }), - qm: Honk.G1Point({ - x: uint256( - 0x22ac07bc4c7d102054ba8dc18954f43d66ed6c57ede3a78e5fe44e80ab26daff - ), - y: uint256( - 0x01a5cce0a2e3607ae4fc406e7379aed53d7cd2cdb0d3a14e759531cee30cb9ea - ) + qm: Honk.G1Point({ + x: uint256(0x22ac07bc4c7d102054ba8dc18954f43d66ed6c57ede3a78e5fe44e80ab26daff), + y: uint256(0x01a5cce0a2e3607ae4fc406e7379aed53d7cd2cdb0d3a14e759531cee30cb9ea) }), - qc: Honk.G1Point({ - x: uint256( - 0x11fee8c098df12a40892852407a771a7d280dbfbab5eeb06b23896095ca7a290 - ), - y: uint256( - 0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b - ) + qc: Honk.G1Point({ + x: uint256(0x11fee8c098df12a40892852407a771a7d280dbfbab5eeb06b23896095ca7a290), + y: uint256(0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424 - ), - y: uint256( - 0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409 - ) + qLookup: Honk.G1Point({ + x: uint256(0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424), + y: uint256(0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409) }), - qArith: Honk.G1Point({ - x: uint256( - 0x2624d4d9d7eac2515cb4b322afc263ddc87c535791e2206eef64bc24024968e1 - ), - y: uint256( - 0x105a6a9c813245babf469aebeafca60e878d41b05f79125dedf362bee561b5ec - ) + qArith: Honk.G1Point({ + x: uint256(0x2624d4d9d7eac2515cb4b322afc263ddc87c535791e2206eef64bc24024968e1), + y: uint256(0x105a6a9c813245babf469aebeafca60e878d41b05f79125dedf362bee561b5ec) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x1cec49a84cd964f7dccf24f37f746eb4660ffa446ba4e79d04582d86fc5fb2be - ), - y: uint256( - 0x16acc276874333a56f75e2c79d9e723e9ac1bb18d1ab5bd579a3ab1702464ed0 - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x1cec49a84cd964f7dccf24f37f746eb4660ffa446ba4e79d04582d86fc5fb2be), + y: uint256(0x16acc276874333a56f75e2c79d9e723e9ac1bb18d1ab5bd579a3ab1702464ed0) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x006554df9837516dfb90ce208134e4b81d29ebf81032b08330501733f5f20d6a - ), - y: uint256( - 0x0ff31f52484554b3123ffc5c911d928e91ee373db03b305bd1350ae27aba4169 - ) + qElliptic: Honk.G1Point({ + x: uint256(0x006554df9837516dfb90ce208134e4b81d29ebf81032b08330501733f5f20d6a), + y: uint256(0x0ff31f52484554b3123ffc5c911d928e91ee373db03b305bd1350ae27aba4169) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x28fad415a8ba66a6c2d15321977f696a033b56580937a63c0be78be9ccdbf00a - ), - y: uint256( - 0x229fa12d35300e25b3095908acfed5751d51e93cd6ecf4af6757ba5a4c540c18 - ) + qMemory: Honk.G1Point({ + x: uint256(0x28fad415a8ba66a6c2d15321977f696a033b56580937a63c0be78be9ccdbf00a), + y: uint256(0x229fa12d35300e25b3095908acfed5751d51e93cd6ecf4af6757ba5a4c540c18) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x27769c90ab027f74a7f86fbe3a1832e41518cd4975e8ba110311664df43f0ce3 - ), - y: uint256( - 0x24a8f977133bcb034382e4cbcd3e335373ae5aac0e67824a2647554a52536b87 - ) + qNnf: Honk.G1Point({ + x: uint256(0x27769c90ab027f74a7f86fbe3a1832e41518cd4975e8ba110311664df43f0ce3), + y: uint256(0x24a8f977133bcb034382e4cbcd3e335373ae5aac0e67824a2647554a52536b87) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x2eb3443efed96b06718b28d1bfbbc35a407b6af60f720ba5a9d0ac78501f0ed2 - ), - y: uint256( - 0x17022aa4435561f83bdddcaa9174723a1e31c11d128a3455edc0b21bf22d334b - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x2eb3443efed96b06718b28d1bfbbc35a407b6af60f720ba5a9d0ac78501f0ed2), + y: uint256(0x17022aa4435561f83bdddcaa9174723a1e31c11d128a3455edc0b21bf22d334b) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x2ef9e66a814fe6821f53a2a2e1e93ac8630a347d7c9fee2afd2edcdc13bc0548 - ), - y: uint256( - 0x0ebdcee17969483e898170e905ff58418ad7e99173fa87c028966bd8c040c923 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x2ef9e66a814fe6821f53a2a2e1e93ac8630a347d7c9fee2afd2edcdc13bc0548), + y: uint256(0x0ebdcee17969483e898170e905ff58418ad7e99173fa87c028966bd8c040c923) }), - s1: Honk.G1Point({ - x: uint256( - 0x235a96328f656f5e8e3935de342e7ffb06d3400d2e11e03ff3f5e9729dec07de - ), - y: uint256( - 0x23b38403e02d9a93b48b7e21c10d7360fd7a39299f6aae2174d47e2df318775f - ) + s1: Honk.G1Point({ + x: uint256(0x235a96328f656f5e8e3935de342e7ffb06d3400d2e11e03ff3f5e9729dec07de), + y: uint256(0x23b38403e02d9a93b48b7e21c10d7360fd7a39299f6aae2174d47e2df318775f) }), - s2: Honk.G1Point({ - x: uint256( - 0x2c15698c01375d97f94676f1bbdb3ba5b157bacaadec11b12cf074cf212f6e12 - ), - y: uint256( - 0x141ec5c8ba7190c9cb1fab4f19e817d9d8f9cb2c0cedd614d4f7820a2f7c4abb - ) + s2: Honk.G1Point({ + x: uint256(0x2c15698c01375d97f94676f1bbdb3ba5b157bacaadec11b12cf074cf212f6e12), + y: uint256(0x141ec5c8ba7190c9cb1fab4f19e817d9d8f9cb2c0cedd614d4f7820a2f7c4abb) }), - s3: Honk.G1Point({ - x: uint256( - 0x22e7871e851cfc6514318d6f16d1c34305dd0e3c0dbe39df3527feda3b0d1eb0 - ), - y: uint256( - 0x078c546da57b7d1340a5a5b11922ab15592a2c3d32553532c318f0a238768a01 - ) + s3: Honk.G1Point({ + x: uint256(0x22e7871e851cfc6514318d6f16d1c34305dd0e3c0dbe39df3527feda3b0d1eb0), + y: uint256(0x078c546da57b7d1340a5a5b11922ab15592a2c3d32553532c318f0a238768a01) }), - s4: Honk.G1Point({ - x: uint256( - 0x17e47cca2b9876b87b90039176b89b889b2e6f6ab55bf5b6ade7026c1886a55b - ), - y: uint256( - 0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce - ) + s4: Honk.G1Point({ + x: uint256(0x17e47cca2b9876b87b90039176b89b889b2e6f6ab55bf5b6ade7026c1886a55b), + y: uint256(0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce) }), - t1: Honk.G1Point({ - x: uint256( - 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c - ), - y: uint256( - 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 - ) + t1: Honk.G1Point({ + x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), + y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) }), - t2: Honk.G1Point({ - x: uint256( - 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 - ), - y: uint256( - 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 - ) + t2: Honk.G1Point({ + x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), + y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) }), - t3: Honk.G1Point({ - x: uint256( - 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f - ), - y: uint256( - 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 - ) + t3: Honk.G1Point({ + x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), + y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) }), - t4: Honk.G1Point({ - x: uint256( - 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 - ), - y: uint256( - 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea - ) + t4: Honk.G1Point({ + x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), + y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) }), - id1: Honk.G1Point({ - x: uint256( - 0x162e6ffc2acbbe037aa8301684ed9e2d850a2c83a3c1a3164453b5c2187c8c75 - ), - y: uint256( - 0x22f0647302fbfc4d83670140b7ec0cd606fd991bd3e7cebeee96ee3b6169e538 - ) + id1: Honk.G1Point({ + x: uint256(0x162e6ffc2acbbe037aa8301684ed9e2d850a2c83a3c1a3164453b5c2187c8c75), + y: uint256(0x22f0647302fbfc4d83670140b7ec0cd606fd991bd3e7cebeee96ee3b6169e538) }), - id2: Honk.G1Point({ - x: uint256( - 0x1fc1d8dce21a638cd9695d5ed2d796b7b1423fe391391cbf0076dabcd5b1229c - ), - y: uint256( - 0x2e4d338298032c5426ca47e6970b8ef0b055728771a8ad6b455f4d3001abd402 - ) + id2: Honk.G1Point({ + x: uint256(0x1fc1d8dce21a638cd9695d5ed2d796b7b1423fe391391cbf0076dabcd5b1229c), + y: uint256(0x2e4d338298032c5426ca47e6970b8ef0b055728771a8ad6b455f4d3001abd402) }), - id3: Honk.G1Point({ - x: uint256( - 0x011c7ccc37d9abaf9dd6ffb88f045f8f6adb02dde453b8645b7a5461356255f8 - ), - y: uint256( - 0x0186d6fa335ec0a6179c9edeb2cfca478103eb4989218cd11ddeb6a4762ff294 - ) + id3: Honk.G1Point({ + x: uint256(0x011c7ccc37d9abaf9dd6ffb88f045f8f6adb02dde453b8645b7a5461356255f8), + y: uint256(0x0186d6fa335ec0a6179c9edeb2cfca478103eb4989218cd11ddeb6a4762ff294) }), - id4: Honk.G1Point({ - x: uint256( - 0x1be8e47ef6bff9941f3febe177d14f28448a16fe9dd81b1c9cfd05bd9136c02c - ), - y: uint256( - 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 - ) + id4: Honk.G1Point({ + x: uint256(0x1be8e47ef6bff9941f3febe177d14f28448a16fe9dd81b1c9cfd05bd9136c02c), + y: uint256(0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x201feccb28b5ddf7440c37e1a8d5676a8f9d7feb0e373436b3413fa9f775fd6b - ), - y: uint256( - 0x03f87d81d9e68bc20ce687e8a53620c9947d06fdc887f89e9fc6a023c8880e74 - ) + lagrangeLast: Honk.G1Point({ + x: uint256(0x201feccb28b5ddf7440c37e1a8d5676a8f9d7feb0e373436b3413fa9f775fd6b), + y: uint256(0x03f87d81d9e68bc20ce687e8a53620c9947d06fdc887f89e9fc6a023c8880e74) }) }); return vk; @@ -251,31 +135,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +298,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +496,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +530,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +542,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +551,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +567,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +588,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +606,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +617,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +657,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +684,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +701,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +765,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +783,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +842,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +861,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +881,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +892,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +911,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +956,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1096,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1104,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1118,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1128,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1203,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1228,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1264,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1278,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1300,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1309,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1353,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1402,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1463,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1494,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1515,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1536,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1572,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1594,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1628,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1651,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1698,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1714,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1726,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1746,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1792,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1816,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1845,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1869,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1881,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1891,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +1911,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +1931,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +1955,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +1987,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2016,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2039,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2051,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2084,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2104,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2165,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2179,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2234,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2251,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2273,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2297,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2316,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2333,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2375,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2385,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2396,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2421,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2431,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2445,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 8f8d5f63f..7dcc0571c 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,238 +10,122 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), publicInputsSize: uint256(31), - ql: Honk.G1Point({ - x: uint256( - 0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7 - ), - y: uint256( - 0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a - ) + ql: Honk.G1Point({ + x: uint256(0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7), + y: uint256(0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a) }), - qr: Honk.G1Point({ - x: uint256( - 0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77 - ), - y: uint256( - 0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94 - ) + qr: Honk.G1Point({ + x: uint256(0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77), + y: uint256(0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94) }), - qo: Honk.G1Point({ - x: uint256( - 0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507 - ), - y: uint256( - 0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda - ) + qo: Honk.G1Point({ + x: uint256(0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507), + y: uint256(0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda) }), - q4: Honk.G1Point({ - x: uint256( - 0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9 - ), - y: uint256( - 0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69 - ) + q4: Honk.G1Point({ + x: uint256(0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9), + y: uint256(0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69) }), - qm: Honk.G1Point({ - x: uint256( - 0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7 - ), - y: uint256( - 0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6 - ) + qm: Honk.G1Point({ + x: uint256(0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7), + y: uint256(0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6) }), - qc: Honk.G1Point({ - x: uint256( - 0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb - ), - y: uint256( - 0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095 - ) + qc: Honk.G1Point({ + x: uint256(0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb), + y: uint256(0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea - ), - y: uint256( - 0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1 - ) + qLookup: Honk.G1Point({ + x: uint256(0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea), + y: uint256(0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1) }), - qArith: Honk.G1Point({ - x: uint256( - 0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a - ), - y: uint256( - 0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61 - ) + qArith: Honk.G1Point({ + x: uint256(0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a), + y: uint256(0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20 - ), - y: uint256( - 0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20), + y: uint256(0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d - ), - y: uint256( - 0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170 - ) + qElliptic: Honk.G1Point({ + x: uint256(0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d), + y: uint256(0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b - ), - y: uint256( - 0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53 - ) + qMemory: Honk.G1Point({ + x: uint256(0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b), + y: uint256(0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c - ), - y: uint256( - 0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0 - ) + qNnf: Honk.G1Point({ + x: uint256(0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c), + y: uint256(0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7 - ), - y: uint256( - 0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7), + y: uint256(0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351 - ), - y: uint256( - 0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351), + y: uint256(0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77) }), - s1: Honk.G1Point({ - x: uint256( - 0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189 - ), - y: uint256( - 0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc - ) + s1: Honk.G1Point({ + x: uint256(0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189), + y: uint256(0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc) }), - s2: Honk.G1Point({ - x: uint256( - 0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393 - ), - y: uint256( - 0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c - ) + s2: Honk.G1Point({ + x: uint256(0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393), + y: uint256(0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c) }), - s3: Honk.G1Point({ - x: uint256( - 0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537 - ), - y: uint256( - 0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea - ) + s3: Honk.G1Point({ + x: uint256(0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537), + y: uint256(0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea) }), - s4: Honk.G1Point({ - x: uint256( - 0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab - ), - y: uint256( - 0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92 - ) + s4: Honk.G1Point({ + x: uint256(0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab), + y: uint256(0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92) }), - t1: Honk.G1Point({ - x: uint256( - 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c - ), - y: uint256( - 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 - ) + t1: Honk.G1Point({ + x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), + y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) }), - t2: Honk.G1Point({ - x: uint256( - 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 - ), - y: uint256( - 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 - ) + t2: Honk.G1Point({ + x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), + y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) }), - t3: Honk.G1Point({ - x: uint256( - 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f - ), - y: uint256( - 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 - ) + t3: Honk.G1Point({ + x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), + y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) }), - t4: Honk.G1Point({ - x: uint256( - 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 - ), - y: uint256( - 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea - ) + t4: Honk.G1Point({ + x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), + y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) }), - id1: Honk.G1Point({ - x: uint256( - 0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9 - ), - y: uint256( - 0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c - ) + id1: Honk.G1Point({ + x: uint256(0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9), + y: uint256(0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c) }), - id2: Honk.G1Point({ - x: uint256( - 0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266 - ), - y: uint256( - 0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883 - ) + id2: Honk.G1Point({ + x: uint256(0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266), + y: uint256(0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883) }), - id3: Honk.G1Point({ - x: uint256( - 0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d - ), - y: uint256( - 0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5 - ) + id3: Honk.G1Point({ + x: uint256(0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d), + y: uint256(0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5) }), - id4: Honk.G1Point({ - x: uint256( - 0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a - ), - y: uint256( - 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 - ) + id4: Honk.G1Point({ + x: uint256(0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a), + y: uint256(0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400 - ), - y: uint256( - 0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1 - ) + lagrangeLast: Honk.G1Point({ + x: uint256(0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400), + y: uint256(0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1) }) }); return vk; @@ -251,31 +135,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +298,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +496,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +530,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +542,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +551,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +567,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +588,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +606,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +617,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +657,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +684,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +701,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +765,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +783,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +842,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +861,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +881,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +892,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +911,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +956,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1096,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1104,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1118,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1128,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1203,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1228,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1264,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1278,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1300,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1309,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1353,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1402,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1463,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1494,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1515,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1536,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1572,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1594,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1628,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1651,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1698,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1714,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1726,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1746,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1792,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1816,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1845,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1869,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1881,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1891,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +1911,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +1931,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +1955,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +1987,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2016,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2039,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2051,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2084,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2104,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2165,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2179,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2234,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2251,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2273,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2297,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2316,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2333,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2375,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2385,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2396,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2421,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2431,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2445,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts index 09a4cde05..c8e8006ee 100644 --- a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts @@ -230,66 +230,6 @@ describe("BfvDecryptionVerifier", function () { expect(result).to.equal(false); }); - it("returns false when c6_fold key hash does not match", async function () { - const revertingVerifier = await ( - await ethers.getContractFactory("RevertOnVerifyCircuitVerifier") - ).deploy(); - await revertingVerifier.waitForDeployment(); - - const bfvDecryptionVerifier = await ( - await ethers.getContractFactory("BfvDecryptionVerifier") - ).deploy( - await revertingVerifier.getAddress(), - EXPECTED_C6_FOLD_KEY_HASH, - EXPECTED_C7_KEY_HASH, - ); - await bfvDecryptionVerifier.waitForDeployment(); - - const messageCoeffs = [1n, 2n, 3n]; - const publicInputs = buildPublicInputsWithMessage(messageCoeffs, 402, [ - ethers.id("wrong-c6"), - EXPECTED_C7_KEY_HASH, - ]); - const plaintextHash = plaintextToHash(messageCoeffs); - const proof = encodeProof("0x01", publicInputs); - - const result = await bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - proof, - ); - expect(result).to.equal(false); - }); - - it("returns false when c7 key hash does not match", async function () { - const revertingVerifier = await ( - await ethers.getContractFactory("RevertOnVerifyCircuitVerifier") - ).deploy(); - await revertingVerifier.waitForDeployment(); - - const bfvDecryptionVerifier = await ( - await ethers.getContractFactory("BfvDecryptionVerifier") - ).deploy( - await revertingVerifier.getAddress(), - EXPECTED_C6_FOLD_KEY_HASH, - EXPECTED_C7_KEY_HASH, - ); - await bfvDecryptionVerifier.waitForDeployment(); - - const messageCoeffs = [1n, 2n, 3n]; - const publicInputs = buildPublicInputsWithMessage(messageCoeffs, 402, [ - EXPECTED_C6_FOLD_KEY_HASH, - ethers.id("wrong-c7"), - ]); - const plaintextHash = plaintextToHash(messageCoeffs); - const proof = encodeProof("0x01", publicInputs); - - const result = await bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - proof, - ); - expect(result).to.equal(false); - }); - it("returns false when plaintext hash mismatch", async function () { const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, From d3488122df673b7821d0b2d08a4154900750a7c4 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Wed, 20 May 2026 11:22:22 +0200 Subject: [PATCH 10/54] update secure benches --- .../results_secure/crisp_verify_gas.json | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/circuits/benchmarks/results_secure/crisp_verify_gas.json b/circuits/benchmarks/results_secure/crisp_verify_gas.json index 143ef2620..6bf35d13a 100644 --- a/circuits/benchmarks/results_secure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_secure/crisp_verify_gas.json @@ -74,6 +74,53 @@ } } }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.614281708, "runs": 3, "total_seconds": 1.842845126 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 2.121472944, "runs": 3, "total_seconds": 6.364418832 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 1.95707425, "runs": 1, "total_seconds": 1.95707425 }, + { "name": "GenEsiSss", "avg_seconds": 0.758238652, "runs": 3, "total_seconds": 2.274715957 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 1.242666944, "runs": 3, "total_seconds": 3.728000834 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 18.979502958, "runs": 1, "total_seconds": 18.979502958 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 48.341644417, "runs": 1, "total_seconds": 48.341644417 }, + { "name": "ZkDkgAggregation", "avg_seconds": 20.006914333, "runs": 1, "total_seconds": 20.006914333 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 30.277848645, "runs": 6, "total_seconds": 181.667091874 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 78.310650236, "runs": 3, "total_seconds": 234.931950708 }, + { "name": "ZkPkAggregation", "avg_seconds": 49.050973916, "runs": 1, "total_seconds": 49.050973916 }, + { "name": "ZkPkBfv", "avg_seconds": 3.850818819, "runs": 3, "total_seconds": 11.552456458 }, + { "name": "ZkPkGeneration", "avg_seconds": 66.056590278, "runs": 3, "total_seconds": 198.169770834 }, + { "name": "ZkShareComputation", "avg_seconds": 52.534038875, "runs": 6, "total_seconds": 315.204233251 }, + { "name": "ZkShareEncryption", "avg_seconds": 114.608395854, "runs": 36, "total_seconds": 4125.90225075 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 251.230740403, "runs": 3, "total_seconds": 753.69222121 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.093863888, "runs": 3, "total_seconds": 0.281591666 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.264344016, "runs": 5, "total_seconds": 1.321720083 } + ], + "operation_timings_total_seconds": 5975.269377457, + "timings_seconds": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9 }, + { "label": "Setup completed", "seconds": 3.273315 }, + { "label": "Committee Setup Completed", "seconds": 20.279577333 }, + { "label": "Committee Finalization Complete", "seconds": 0.007173125 }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 5158.12970425 }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 5165.210807791 }, + { "label": "Application CT Gen", "seconds": 7.707659625 }, + { "label": "Running FHE Application", "seconds": 0.0710595 }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 835.140242834 }, + { "label": "Entire Test", "seconds": 6031.697821958 } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000821730b5f6c7306ca00000000000000000000000000000000000000000000000027d5a4288c59517c0000000000000000000000000000000000000000000000061a32d74244b4e4cd0000000000000000000000000000000000000000000000000002752ab748caed000000000000000000000000000000000000000000000005a4f0140d4a1c7f7300000000000000000000000000000000000000000000000512ac80c5fa266d0800000000000000000000000000000000000000000000000d400822869689e0ec0000000000000000000000000000000000000000000000000000a3ecc527c1e200000000000000000000000000000000000000000000000564b7881fe1989f5500000000000000000000000000000000000000000000000674ecde0680d8bf4900000000000000000000000000000000000000000000000584cd67f303aef6c30000000000000000000000000000000000000000000000000000623ff53a7e1000000000000000000000000000000000000000000000000b27d89433be674a5400000000000000000000000000000000000000000000000d03b476bc4cac62c900000000000000000000000000000000000000000000000d562dd63dce28dce600000000000000000000000000000000000000000000000000007014317d4d7c2bdca3a8f64dcf8eb8b35863e69e18fd4f152ed73eba57c25990e80b7c74b79013d6c8d4d7310cd110d6e43bb18fc56336d2e4da3740b50e8523d5f9594bcdd812036507369a09143ca35760ebd0173b2c22e17b4dc2980119e4fbf1ed9cf2a9021fe43ba1b07e24480cdba82eac2af02f2e755eb1b42e866b12ef874db8f56729f0b56939196bb391b63f552db3f3c7ec39a1d778f2686df8f01eacb03019c80d8bbf928d3be121e24970b738be38dd033d077d66095a42d4f1494b44eb7df0074e45e1376a363bcf636637b442460155ece719bb40d1fc476322ceb66ab5261818431b676c3ee34900e20139f2ce04bedc6ee159747b931b42f2b22b3d7198079fe5ff134d61e1d32094cc83972db87162171f68aff7e77845fde2854f3b9a0d21b74b20d8c2ec809d9db9e189051c0dd8557ec1661fa1a1b0ce322590aecc23e610a0c95bf8e2f1ec67fad3b50a0d2e04a267e7844e7e78b710b17602724a2b1fd0daf5aaae67ae6e02faeb646dbe547f0c699e2cb1379f50d28f36ba218a1b615a728d5b46c9da5f17803b7b0ddfaa6af8a8d6d945602cfcfdb1a8c2845e10ef52b08e4afe6f9717aa1865a5df615627ece311d2bfccb7831e0631a7271a08ce5712e16458da4091348c134bbbc0f6c546f14c8d9c231ab7975bccb9e02e0c991572aba8f415ac14e2e466eb01533edbb3a35795beea6327e635c6b103740f2b82ceef34e542ab504d3202e68b230920bc61d94d1cf18cde724d06e23a482b31011ca389f49559a044f8bc99e2eceffadc140cae4937d763106987d7fe30299aea525ef4ba58a7d10fddd62430590152bdd9c1fe3f5dd865bb2ce4e22a65086dd1e3b14c653011e7a3c89c4abaa1ed50be316fd87f42661de510e98dc9bf0dddcf46263bc755e1f5bad01ba6f03708b0bb47ea5aad02d6a9052b1fce2f05226a3e2e74566f10fe938081e85af521d530facec93e917a64abe8a781e246752f984676b56b5a9c17fb37ec862c78c596c128c75422712251c9e25f0fa4352a2a7bf93147adfa3937ec9733a4e035e4a297fbb1e9599d1de9a59b617dc808f117e08e894d7ebdc106e7f065fa3a096cecca51bf047ded0439a19e86d1f4a358099e717b59523313b12586636c72a0028ee88c7cbf053fbe465dc0d546b698cf132800fbde649562ed1d1c65b70ad4ba7497d90bbaa8dab5316435f6473614f501bf945798b6e787cba40d1202870cc74db20387287ff9fffb4cdf7a0160ea04251adcb89e4bed874a70342a96386f8ab23147019c7e3dc8a6f2d0f6ee77ea1b1683337f4dbdcb041c320a5df0f41519161ce81427ee42e7e55b381564cef1ff1036c245bd9b7f0292937e19a58c6f917d8bc00ffb2c098590ffa91e35aa88bf0f29c027c75a7685fecf83028b01891bf2582da15f30dbdadf169772056758b112d80f494136784c5e55d2ffd210cab43b12b3b22efff7fcd159f532727da6ec05f1b98a8978a14a941d2489d0948799285fecc08732988a56e66e352362a6be2cc8b3127ad5279b02a55f57f785b7e1db4de806714cb6cfc421b1af185f408d2beec04fcf9b045a9fe4911bab199445b8cabc822f63eda13db7ea709458a02c27135db9d0e4ba34442524e335b54395b3c5d1d19d856c4a813e0fc2303a45b50eb4e75e5e2bb32813c7346b9c9c1c0ef2b8cccfde4c1c8f4370c89efceb6be328a05d97d39aed2e4cc7d7536b34af28247ede4000fcc7b428147375abfdfb5516e986f5197eb977a865b855e9772fff94c52ffdf99286ee66fc4bd6ff9fa6540e0da9efe06b9e2cb8c70f7bed562275ad9485849152717537abaa41b09aef482b701578723527aa01c63295dddd454473177bfd0415c06d3d4e1848d50a58252bf4553e8afc869321e3d7796ba736c8b389b852bfec26444709e9d423192bd117d4a7cb64bb293e7c7033de3dc675267289c8ba6fcc0ca6ce0b9050e79e4e2d12956851fb28b58225eb8c0ebc9a45158c59a5655fdde45136ce3888dd460ddb15888a69073d825555fcd88eb1a3d7f67a4ebd571c54d2d1274518f65805c2382c12086776a4e11771b7d00b480fb1c5a78c300cd815f8039dc45ea68e61c51e130a50b94972ea631e451d8eb55ce9868d50b4e67952699e020fe4a972b44a36115a8998e760ca80bfb4b64576d97e16bf0d1c9b703a106482cf6872973e94520ce2037c03b5c1cf4892fe2c779650c50d7b88b19a1f6f4be7f5c78fb11783571256a19df3f4b96c9fce648218195e60d1c2e4c662d66ccb18a71bd0a9fb8a8b23c3faf51f7aef24669b70e1a4274abc0f66b911c67f15fd970be027f76d1aab14d557ba7d4f268f95afb5068341ba0900e85cc7d322674c057004d6fa17189123eae8a63d39bfc00d3630a696781bc7ecdc4a6b1d7677ca60c64c32d111d3c10f75c8f14135d4faacb046778787802723936036487091e65721e7feaf94a87d10cac1095e8bd1785f16c5e7f0f5fb9fbfb5faa9918ce2a047c7c0debec9fd5f2c41c01fe8b3062e6fb6e6961c09ab4db1da10ab612f4c5a7328a5209dba00950b62695a4af7c7304b3eacd7d23b4de36c705a55605659d07d7bf79a382923d82ab46fbaff077b68f5b592be6cac56d52f95b7f67123c6ef30cce46bb673b92b0ad6bccfde38ae2deb094a64332f2645f4cde17c0a64aa6cf9b79a7ed828fee42cca7f36d7f6b946dfd1e01db6832cb6f4eab7ea793718651913922d4f360d4b21f257a41c05813fdec7e7d01cd94552d86eae1bced28135690ba7c02f94c4102e5902efa5fcb489bba7b608b977362ed8ef889df360bb12d3f6fa737cc23ea410e2c92ffcb685b442e3884384753e48b3af21056bb1eb92dd975c59aa54ed01161b2d1f8b8e48050ebebc4226dad4c4efd3ccba5c481e80a25e903ed9bdd7992355c18cd5fab093b281f89fbcf4c49a728b3339e0a50b7c8f3eaf38c0a45a241cd2d8c6780c99d0506d6db1d8b009aac9b761cb438e732bebc6c473618f58a526589895742bb0bc9bf2157b0ac83056acd061226b5155c952999f617205e6231dd54dfe603fac9886d0279e7a02a79d39fc16d9590496af4854499544f0d61623b4c14179daa63917368e6315d7c8044998be25f1fff60ff99cebeaf702094322d12ecbd646a3e725894e99bfa905064f065a4bb9ecf069150adf4f6f234c7203c810c6f79c786b859baf6db1ce8395ded182bfce2fd1385b1fae68ce21305026844979fc3f6f15e3f01a641e070dac977d4c4e8251de5b25aeba717b43bcd322a34d4d3c3c7f307f3cbea1f6c4ff7736b1fcbca1b856a1d39a1de2a09e2ff306d5df092ac06ba92c3a7a18fa39a0a90e5b2c1d4d8645f0ffca3717f752afc42eb754439548876e6b43c3a5d94e1bf5abf0e7f9b5a6686050cbba01a352ecc21598da0ad02780190cf24cdcbc430d5aedfc4770abe9c6863cfeef593d95d28b1b050913d0e34830cae0602973282d94fec6205ec8756b4d696623c67d33e55e05d1cce77fdac4c7fdcfaee946a7baed22eb73fb1af5cea27644c13e2e249b6a0fce2f7340ef3aa4e2fa39063e6c8ed748a9d29f33a38f4b50e12d60c03433b81a80e41a565ac3c7013a1e851151847f3bb22ebefd9ff335ff703efe6189df8611dd23fff4444e25494a43eddb53fd61199cd4c1c04def81d6f77187f8f5e3da1a1129ba1faa492ccfa688a8fab7566278f412deabe1f69458de9a4991000314172a67e9eec385ebcc15e24e8cd28e117a2e885e418a5eff75fa6e0af53d4782282f17a9691bb7192b5b670ab7a577b331366962dd0a36346f28b8f56ec241ea0b6552eb0d11d855088e95a118fd271c8a3a1922ea0bfaf0afe9ea5c758e20492391a1721c25d590aeee4199f47b8e5c32ffea485cde82f7392674c9de1aeb3c183f9916e9789575f1b0b461ed9905831051ce8b19952d6c331efbf48b7dbf4614a5b15d0e87af9e76b285fd9ecce3107a983c726ae5d3d6ec6e2baeb34a9e390ee6e7510fc9579db80f9d8ac57e1cc886eaa4d2e4afe8430ff03b6c6f3aec5b2ebfab036e1b2efe026beca2aaeca416a017afbbf80ccbb2e72791f9cdc6f6b505b1dae159a08ee04351f5f2f240edbe45973e734d98f6f4cc9e2b7da8da6a110f7e97eed170300d1f8541ad1e1cb3bf6b3863a4cb490cb5c39e6d4faae2f6ea0082ec6630d36696ea41f90d0d64da3c8ff4facd1cec120e8736509315d426770883f14157bd101f004e3e63407103842843b512eeef44a77743bcefe483286303ab560fa84c14a40002cd93f22ece518b999cb3121c7e1df64eca2069230193028e8d3507fd7d7be0e586300d994405ce6dd9895c747b49f51f20d494c916c808ec85a1e118a4ba5f6448c66d7ebbbe036e81080e7fb9d38cd1ac591d5667dd060cdc04db6517473b7814ad9d757fca4904ebcbc818ca1f22ecf674dcc0c8ec1bb2763dba05f118fcf7500e370108e9004dc46a567294751b7ca51e51229b22220d0d21392e57177d6e2671cf3e954eceaca2cf87918287bfc1f6bf533708943011f277db9e4f9d9ce32ae3f3a1a3343d87d3f95b52d768e4848a92aff7dfed1aead60fddbfb11590d84445c4e75305bb2313c20df2f95eab7e64eb085ee372282e3acefad52cb23bf8855d760a5ded264e7cf806f650248c6db9c54b48e80709642a094184762433ca772b01c277a6fbf700c2046fac1e7c45852dc4c127ec0307dde473f72467a910331f49b44d665488a4c88895342608a78ef07cead2c305ad0fb767e03b49b8e665584ecbb8651dcafc1b5c116b33ba927fa8d15b98f5214a6f4c52b2e56646a8d9cefa2258846c3de653e2297a4051385c199d278a5926a6a8f3f0356900b0f226530e88bb4714c63d444b3dd57e11227770c75d1d2f225236f50e50cf07d29626531dcab3db32b7c1a3af3e43febe274500247334660014e70f3347b1f65c35c55630d4db54848774e2c140eb7c7dcba10652028cc818c7a328564594c77c3ca4d6cf9b0ce46a65cd4cf70a6820eabf65f863291f0f0c89ae9929974dae20ebeb9d518dd20b2795765cf0b0addfc027806a744965d41d9fe3385a89d328e2f8a5dd616160cda986b7d7ab9ec9543d99fd0f70ca38771d00e76228c39527af15a012f227bff39d231a2682a9bd9c4b45ca1fc2b260aa250577f8fa880edd4a07dd8115511301135921bbc68b53bd2375c7635fd9dee70dfc0e14e099d417eb4d6035da9ff7a9f647f84df2437aa344deb5737717058d0fff3c23d38e8d2548ee3ca6dfb23b0c4dfd22cc50c86134291cda45e0131e1917765092935098bb1a016aad380ecf5edc52dd0f95e566d538900955bcacc4342d16f512ab3e6aac573cb9a03082605ae8a4850e455b5d1641c03a37b0cf6ff90215793fd21ee0fc330133beaa6de3b5d59c40755ce8d0cd2c434b9a228fb79815ac089a2735a487f497d5758fa303de789f3ae7a09298147bea45a9c69ce19e2d058fefcd8ca169e2ba7760c96e7af51de2a28cde2d2174ad0a910dbb954f6e2fab7246831353872aec29ee4df899bca2eb5001464c97b1cce8c138fcc94ec40c8e76d15d58b5353e6b3f9ffc5b871ea1d2cc1fc1ab411671eb7d4f80c28b051dc71f34aaceee78f29a56e4529ca51c856472af3c41d587c25a3bb5ddd190062840ac3eb0fe54d3b1f28f63b89977405cd88ca9c73df613c7aaadc6965661c70747b5fc78ce643b6811e78b86b7f68736e2b356a934001d2ea264a89cfbe99a12dc049b6f9ec05d04083fd34b5647bd585f1be04af90aec97ea1e1ee9d42b841d7f31c6fd3853b6c12510c173a710adacac5b23fee0d5586900c83d9711955a1d5d41d21d27fee28b4989409d645d05feda97d3b12b1bcf31e156c75bed1d3d299392602de76f62b9103cce850a2b5a875ef961bd2f58321bb605928c80a122147f4095ff4fc0c4772cc9eb0052f677550724a61d0bf0d328fc6116e13e20ba1a7ab9f9f8506aaf4d953b7c13ada2220f7f6f4695fffeb8a712b5cac9f185ad15a710203071669acc41cfcb817ac9aa6d2ed7c763d9de8b8c7ac7374b0896db120a5f2e718e228bd07965ea30f302417d9f9ab3e01991d4090fd2b0920194ab038128867db1f90f4a79b4586f7602b80324cbacb580b34263c724430b9b91632d8d497956dc8231e14a4d8a202676f3b8a272bb1e26122f025c4f8f78d5c72c21b998aebd00ed34be2d76e4a43006fe81588d6b23eb5bf4c3367285c9a9ef2927a7877682d3af2d5e33c05808479f4521b3a17168e6c97bdbddb7770f93d2f01b08b7b99d331e78b71e84cb19252fb9f9f1ac65e844728db5722e55ab95b9f41edcecb4f31deb949896cd3db5cd0dc2ef15e4ba0eadb9bbe2657b321e467ae20c88064c8c15e19067e28939f8ee13304d7283a98fb49625609483ae02f2a30d160db7d5914120348062db5917327da32d027a758811aa7fd1c21b6a1dd8c75e09d22df3f7027626e95fcb651e8b7559471fcb5ffb9162d550aa82338c51cb0f02c653bb8a2b59c2959f293a4973748870484c05d4ff511fa666604692f73cbe282d4e0e243d6d9ba21e950ac205857f6d41ba6073034f315ef27929d5dba83205f192e83a1e5003de8d1c38c6dcfa2593bba4898090ff610b0890515f9bd44d06d0e8c2d93df6d36626472b3ceba0994cc76cafb660d6a692e7bf0559db1ed02fc0cb1865d7d25f89fb247bfa0e1a3fdae33f97abf5ae7d9e35033050445f73244d228055862735caebee5db8a119a9d8d11a7035555ada83e299f3387abf5f0000dea04c2732cab2470e3e04688d9f1dedd763e036634d28ba9f8e2c5240ca271898be20fb585b49b60139770919a34305e52be70f0d50192770fc02517f652e1e7ce1906dc26a8fbde6939f69b2c0645b8cb4a3954ad2b43918305a27046603a85deac0c770c7e523137f35e3000a0bf96b8c329525f65957437b757c262a2bc6a9c8527c1359d8e598afc781fc2e7ed7501a730554c58d2a552329f99d5c2635fd5fa08d5535ca03c6ea08662a9c52fbc5959b1a998374ed3f624bab2b5b2c33c8f5aeb856586abd239d13286104a2e90e79983c92ed579bfd5576668f7214523b4f53f4bee84986ad86798ec100d603f863f155fdfd2fd2cd72f5676a852c49103b1c0c67f55c4af21fafed6d1d9ed79f863c9807eb055b1777945f8d4f1e0db4f8234739e50bb75bc8fde5af7ab0688ff97d67209fce8a9b39e7b4d05d25548e0d72bccaffb3b604d3b33ce22039ed319792b1fa674d396a6c9786dcac294231be2234a234be2524f2f7dae91c55e58074b0503b24530a0c6e6297f19b0e695dacbd609ce9ca7cc5b2511ba837f4eca002f7102967d35389efb30000cb181f2a3daffd5d7f9915bf8dd1de5d4e7659638e9584d13714469a9ba5d37d7313784f05375e9c5e34ef8219292be23da93ad73869d0bc71516e4393f5a782062975871276d39f2f41d36b81ebbc221648befd2dd8b9ae5610fabeaf54f1947f17dbe4490a05d99f3ac74e0d537673afb9d993294d2aed3e7e293ea0d280ceb019e6f0704f9b34dd1a8da3dc616c365e09b09075d2fef5b1ede4a3bba6df095b1390ce5a31053ffdb83cb05b9209d37592156cb464012744e90748e708378d9c04416b2fd032528cc00e516f1fa6bb6bead7b0d0a69d97e0a14e3b40ada5065b101efc0f1f4cd612b723e18127513fa2b82d5f5cdeea70c60581191b3c7cd81f1cf121cc958060e951b4e6c76f2d557bdddd63f9f51676352d04e0174aa85a900b4a92fc1ddfd06e8006f1d78401e5f7390ace2e425bed97aec36ff7e00d2f6525871edfb56c38063860c618feb09c8748f76f88ea04b6ed77e5421025254d1b2ed05884ee40dd3e9536f678676913dc230188ced021bcb986b6b18b475fe0ff25ca62e1660243f9d9b164881c2398ee00b789bc8c4444d3682420cb869165b30e224323381cb5cc73620a7a96119458441256a6229f2bd621cb86ecc29165db17aeae0846231e6b67942cec4406b326de3bf6da88d906298be18f50763420171db37eb6250875d716390e1399175ee4eec8b0bd26efb179532d87783746b5521a5304ec07e9daa765361558c3187a2073c503c3981ea7992d9c60bcf15ed2832f492d65ba407662a090ab3ef91cfcdf450a588b143f4cbf6fa16010ea41310c305c41056c1e5d407b1eeff592b7fd4f7321aa7e2be12587c37faba2dae3b5050e6c1530a67f22db6c8febe5f34295ef550fc8bbc338a40623dc6065c708338604f699048cd976fdb44878c7b1059cd85f4b91b964b8df3bf9c70f3c2befd9322551cd9f2b50fe2d058916efe06f883fde51721cc8523939a3350133f533f1810524dd023854b3ae2592e3c34de948a860ca68eab2e7515f901ba4ef130450802767ddf18f04412c57ea96eb43689d7148daf50f18621a2a2f4aa76ff98011bb12d62e69c1c4d4a8baae33a8117b84857e317f8562da97c5769136574dccc90706388d13056cff5c7444a3beacfb4110d60cb4a41e70cfcc8de69c72a693c3c22cc4374fcd2cdef089cdfe6a0c2a0e8f8acf27d8e36b650e13a8d51308a25a6829c36bb3cf8ad207df79717b3a3fc45a0dd3b5db984b8293dbaf91f785fe0fcc0c2b38ec1232d147cc42ac3808a34322fbb5f8306303cffb36614394275526e41f473251fd02c680ab57b99de93a2ef421ab91e4426b4c99f3c6d03504de269b275d2cf9faa91d300dc7bbdb3dda6da71347133816be7b051d48a33903efbf4407f90419f1332cc03e9fd8c41e24a26c8f42481b2e9fed3327ce1798263434e51fb358e785403ba67331bfb0f3133ead7225bd117acbf86d18a8703e1d3ebaef2281f87c90eee0cdd19c503c9e39020cf0a6f0793d93dfc23d0167592855aa6516320c766eb5089fe5089ff9c2a5f43622cca3fa745cf3e61a9ace7b7e1c1efe1bdee1c9bf8561b342a2cacb2584c6ae87cbc23ca7f21561483dc48e65ed74d70e36d273946c8af8fbfac07b8067c7537725fb15dd74d6fd8e06d9560d04fe9e0bb9cc66a552723894d500bcc67d9dbbbb2f7def521d214cde49257da5ebba58127ac3ddcf681962da25f22c4452c92bb8643d2d51cd72af07b36ec40933aded2a7ca13c7f42fad892ec2a215c0b050a497585d5acbef59321362e9ebc54f7d7299414c10a9a83fc9beb917a3272cbfffa4e7da3fe84a0f10dabec1f7e5a897e0962a0e842037b4604c054cd61aac3a07fc1c81f3a72e51e2ec990d4db00b9eb14f32933b1cbd476385d2d315eb05da63cf9aa7392e86f8789f1eecdc9aae8fc14541b5d0cf84ceb4c04ef83b692a470b986b75ae8fbd52ed926f7c50a3f0a5c0025385e05933854303b5fdd3eb968d912173130bf0b081ba8ac3f93cdefc9a8143fa61d6377db816b1367c023307d2fab251d5ca6e846a272a4577a7fc82d0901acf57ea102113af63c159c1f5d0d91078ffe6f22541bdec846436cb3729c8126556cf495ef535047d5bdf6cdb5aff1c08ace3e8ba81eecaaee4bf99b80bf98026d93bd1b87cf7eb21a4085647627e2b0b2504884d459721c7b979153b14d161ecf00bf75bf12b4c944c0096897bc8c53526c746135500931ec160fb77114a317c0ab237646eff1345dd26ad855f8d7f7228ad671ffa4793f582b208bcd2dbb018fafa828663cf93e8ebac379ed326fcd502f1b27bffe1912716dec34b4189d2949271b39f8384eaaa45e64fcc800b5f805dcfe8e9986c26d7541bf962d1166044483913e751c75bae08db36edb57973712fba0c80d6813f1e051c2af60ef6e117a84999eb5ede01635927eed50f201635f19e297f45c406e75470fbfe8a40f0db953affc8f61b592b5b0bbf515eede750bc8a0593edcaf5959a5445a1ae0c1130526a514a02445e3a6978f1849f50a7c23db9340d42577892f4d385814e49b151466ff20534e74439acf791d130b0f765f9c9b2ab66fda9098c7771834d71e0ddad7d678af5183635414a7ee104cf52afc1dd2ce8fdff5ba21534f4d73667501ddc7df289a79ef1a336f930cdbe947883fab991b26adf61eb7837d582b8e9d15ec335ab0809126826fb09abb2da87e9aedca01dcf1a662b74cc555fab8b65c013fbe0dff90977f2a74360eb7fb0e75c59408389757e17ad071d76c112ffbc4014b0ca129f864a2489339ed260cf0796380943a2618f3b13e66649fb04104ff15197fafa3b047eff947a782bea063ee95bc5b8cddbc7db60661db0e6a47be4619fe21e5918752a3f48836a6630ac27392a10fe2b6a6b292903709ee2f82c75e0d667d9b51d5ce9ff23fd6c7f2c5a87f087748824f9bb170c33a51a704be5f042a94345fd5fa26e80dcc485aa26fa6c6061d6cafe422caf1eac52f8405a6c0aa2bff08666e2efbca4f1baed7f5532cb579708baeb3555ae6b984fab00610901e1ba452a848424b71e55829122932331f5306b62a5beb24c70237eb9c0e0207591d783524c1a28c1fe61f050b6bc6c300011f151959e387c0125b787cdbd2c7241f837fb79f84889b30cc8e5e84cb49a895585e78ca301052c5df2a4fc873db770367667202002946d7dfbf953f113a006ad4837ea696baa9272c58c9af1d6d8e1f0f21da94147cbaf178fb414d0a77e22c33c3fda3d0be7ebcfb9d95daed750008c9a1d8ddc601df5867b8277024e8c2f447a739164cc63ea44c03ad55282f181cf08f3933494d3938e6937267d1f54289a4e1f19d16abd449ee8372fce0834b212bfd7c33276b09f5ad9221383f3bea85ff25427d5deb17e7a510d4104e32aa160ae05861837210115dca42c986c81002ca19b8d3645d1b473134a160d34ee3261287fd51ed3dd9518c3ca8a42d99bc095a056a3a51411fe822eed25ac7b9392e319e63cfbdfa8c25b357407e9f6e2e249f9a75b5fc17b8940b10c7a8bdc0c80ce7c78c5a7da26bef638318da682b033778c232ce708d33e025782dd268861c06d6ea5cd80d15066128b7b0b5d5b6f51f240ea176d0580ef7b02eee47a5cce712042e07964c9c052cedc831ad39cb143a888041c8aeca71ddda00e103d609160b7ed571aa0f7dc432de1024a9572b2f46029fe8563ff2fe6046cfbc4589099a139ce636ae588318153e65aeb57232762e7be53e82ec3c6f46ee4f923625378f183f5c778c1d9420a1523b518c7b970ba291de12f4ee0d3abc0daa36f7faf05901731bb21c7b0c1e3fa46a76e005632f8eba62e811233000cd2539f44b06619c0391e67d0b33b06bdfc98775458a4293c9515b87e5c25f6cb6d29918837096890ed9428f0cbbdcb8eb0edc385318bb3e61aebb5774cb91852ccceceae70c38cc2056a75e1d63ae67c774a818ad111bf39acba607cbe4864fe74d45aaee92912e237f27cb4f8d40f7029eca2bae4caef946b9549348e37161a6e56168328e03490e955c18c7bf6cb2ed0f8a4c94bc635ba90724e62b29de6d7fcb2249d3951b32012abdacd304147bf0b87a684c56a84e4654a1fdb1c2109d4210f7e9031f3ff9280148dad4fe9dac49cc6815a9d2910e9676cb332990e6564537f5a787e126bb154cfff073cfacd39e74a57c4d0d382155eea6093b96ee0e0bc1d204f58742131e755596903ab9eeb3e947677bf81b62c73f504acb1adc7e4dee30c034ab75790ebae3861e8c094437ac0d5cfefa3b3b0dd75c18b443e46060bb69f122660a7f0d8147ccd093f2aa6b1ad705f3791229891faa6f27122529f42e149eae990fde118d8dd0656f0fa9f5267beb6ccc40fdbbcbf34de723c173979387831ba95cc811e1c79446cb200b31e31f3200a287f582be616f920f3b24590529f1abea7c180a6ce5ab8515f5498960440ddf5000079fa233245d1df8fd688ade85739eda4906703d70e7215b65746facfcfe1aa1ab8c7c817b137661d535ba08aa63aaf44f2b87690dc15322b2c5cb26d729a557bf30748c7dc60c36a9610c9e758330ee4e1efec3393675cfce7c64da0c1fda8ecd235b8c76e3681cff99daf8473c686d1f0bf6aabadc7acc20b88f11869b498b3d839f679d443073ce6ef84512ebed8821286504bf8db57ddaa7f07b0ef748604de3498c5a8be6c3b5b8121515bb8306782ca30bf541db60bd1eaa09ed005970e3c80a2c7c143f6504d10b208ca352b8880e915045e50f5ae5d45f7401b0f70076e20ca6635e89bd950b832ff0aaf0236e206fc268a7f893c058c73030c70f4c7deb51ed388a3bee3aa16534d5319836be14f0addc9788e212172376c5f8cfae54ee2cd8792ae628061ce47cc1a22869761305fc7a5369bbe86b6aacfeb72427f397e40e8b901f5b3e292d7d29160a11bb05979b806f46ca206ada7f1ac05c54c4f49cfadcc39cba3e49e3ccc263ff77c319ceb2c976e8f0ed37f6a73c371cfd76d1b82af0a1e58a6f8e6c0068076db9c229f59e929d9547d2de2db72aa9b89fd37dc41561ab1809614d396d06eb5689f00b058cb17c2b3cf46fc636066b4632a5023dc55731dcc76db3dc1a0a0ec84c900a469a7d17720805a3c2bc7ff493a2df926d49a394a7a0c45c7620f051b2ef14160d19492657bb67d1bef596d29b79d29a4babaa5428795879ef66d3aa7dfd7807592edaf9d568afc173e452e112179d987725eeb41f1182a9f2bf32db42b63b1898abae8d596b33efe7b6607af0079dac5d356bd7cf13d10d0a4e0768f2114c25ce2889ce6c34db05ff06fbfa4b955b018546e27cd0bbfb2bc0cffa0792cb5823bab8a977dfd68e70f068ad03ee38aae0e7d49a8957c3878d643e6d0f65fcec272c725699c16e846c8638f5cec0fa2d981165b3bcaa7602938f4a2149ed71e10cc23a9c5e7d0a3f5461709b23cca9f4eef68bfd06a6fd5e505e756ab4c40a8e2859e0f20337582b5f4556a47c503934f1b8ce4ba7b27a31560d3809029ea2d2067381928499d6943b040cef2cfc40f7025ab51d66d46db1eaf2dba55ffb9d472f04933ae5c2edea96a0468675bdd6c86d3446009068ea089303a1b9d053a8841d98c5b5a491cc1f8e8e260a3655fd3a6b3dbc20beb8e874272a67260b49afb0213e712579743fb62f8ac169ab6500175cc164c2a5caccaa675159759f708016141c4e101152c0e8e55842f9bad78fa3d8c4ed129f91d6450d6c06e85338347f0f6a91a32ba22a390cff1ceb374f5e2b6745e8905f0866029a4c2f9ea7299cf31ea5b0b47ee4d6329b1ba0f1ed46fb0be938a45a2db24560edd0dafb2f1d5526073075b6b2bb62555d44907340a599754fb868566a768b458d6f6b038866a2512c37b7d743fe40490af6e2267fa03f03e2a8f9b6d95650221eaf0b4c87ccf6d505125e1dcd88c3be809632f750178fb8b6bae40b12c686a256feeb481b20ec7a2024b45fd5c00f5608c9cca96508b2425fa584455c1c79dea9c947c13e54f2bd0b77f70ae6e1aadb54cd3af5dcbd773c96a36c60a6e94d41f051284397b36e4102431a5c1227d7a2e5eb432dcc11f0fd5884c17eecfe66a1ce2a22b0419b751d117401dabda490ef21888c13c136e09941774ff0b070e9b24e762ea7ff7684822e8d26105cc2ca0b833708f06574954f6b582e8053824a60dbf179e50db6f55c069244550b03c11e5cd22653891fe5064198a330115698a1d852f57c32eb855423c9db7c5aa271f155577ae09f6e5a7e32c2aff1f5150feba43b795cc2584c07001cb655353eec53d999060ef4380640747f65e57196198444330db72f8081f5202a4f8e4f598610629f96fdfd5734ff55a7f4031c8538f9fe80d1dfb973e8d61beb98f1f854d9783b319368ae6f9a015484bbad829d30cca00981f840ec563a2e24c11cfd9977d1f787fb0a395ba4b1acfdbf6f2bafee57074e5328affeb76204ba93ee5b82ed1dded622a843db90a4d19c0b4ef46867161b3f6525493b3a3e285408073fd97e2f53fdb86c06e9845e8d410462857a2b9243b348ad17b662cf06686483db4b559d400f33726b232420a522d3a4a63376d60b56f5ee7cac6688203272656b35c2a109ed301e8a298fb397d7b66d22b4867b9806269b28a5c16d2c366e20f6c78ecef995bf41e82d6fc7cc578e293bd917b60d9bd6d1dedd21bd12b872b9d3e443055b534b753d81d39377c29f810995cf6238b69779a358fb6208eadcd48848b22b262ad65f2682172834b865f293d2fc5cc95538f242b21cd12261023dcc253992cbc1c548d996c675b14dac9b530fcc0bf78fb90bad6027e52502201fd2f781fa9dcf4da5c91b6706f2eda965b330521737df12d13d350c2506a779924f2bfd8c634cd73c84811da8534e0b14ee42996d6ed01f879d4d858605adbb356eec9115a7e0a689e0de4c27be333631fcfa9fec8d02744e206dde6110f97142186a44a6f4f220b1295b4e1b0385b0826984a145ac28bcf89a9c3627084ef803b0cae77c7eb29fe2d6ff15e3762928f2f1100f4a8b59adeb81a445e70c54d7b8453eb39ec2e05e612d1c0884eab9b6d8792606e5ae8627d0f4d44beb2d4306f31b6cdc617da0f5374ae79900cd9b9f693debb14d59889d5b802fb8471c0a414faa8abb86d379cf24a883de065656be4205dd39718b1dab3ef93ba2e5107d2c79ae5b564bdaf2971a6ea04febbcf5e2c7b3e3743d8b37631ed2ebfc2a116cc9f8b27037cb4d7bd4078692ed74caa2a1ae3458d1eaef27cf48870bd95a", + "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c91109676183000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008f26088c208eab68279f6f45f234d271000000000000000000000000000000008ec0f15e98d9058c0e708be73acd2b9d2197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f2a318f2fc85748ea90c49625631654288c77791d8dd0792e201c748ca9863ac417b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e2bad40c7a2fc975f2e4811607e4d5fb72361dd1af1bb2364be799270aab5c6f623db21355d312ad82ff6334e39518d7aec226005c56d124daf506a44749cf370" + }, + "decryption_aggregator": { + "proof_hex": "0x000000000000000000000000000000000000000000000006443e4dd7cafbc6fc000000000000000000000000000000000000000000000009a5b11556957a2fac00000000000000000000000000000000000000000000000481d8c87ec8d9e44400000000000000000000000000000000000000000000000000027e3c69ce5a8800000000000000000000000000000000000000000000000c89919183396d2ee500000000000000000000000000000000000000000000000156b5f6351b4e1677000000000000000000000000000000000000000000000003fa278d2c3f3cc83b000000000000000000000000000000000000000000000000000257547a6577fe00000000000000000000000000000000000000000000000a7acecc082a3669e1000000000000000000000000000000000000000000000004e4ee60a9610ccb0100000000000000000000000000000000000000000000000139a7b0dc64569eb80000000000000000000000000000000000000000000000000002797aa349525b000000000000000000000000000000000000000000000000e4efb10408e5e22300000000000000000000000000000000000000000000000565dff2497b05203c00000000000000000000000000000000000000000000000c3ee606bf31b8fa98000000000000000000000000000000000000000000000000000144da9a140bb701dfc1ca1fdf6f4d4992fcf7bebfab3317fc2caf844b02fb8ac4436d5adea45f18531115019e88ab0ae6f13233c1014519feb8d5364b2b488e9d836182eba9d800f3b9f16b517a82e074902c3c99bf34e7be3eafddb913ebb2c8d05595a159bb2a543541d15947ed4f1c1620e84108ea3ec32503ad7d073162c30b94474e387c0cdcb845177847e28da58f2486d85540c5484ae86f2294cb46b4cb84d0aa3609083d483a8b920f898636ed7798e980729ee4ba163dbecdce8b02670604ec9b920e6c4aa8dca3ce558f7cec8fb7f4bfa1b0d4371b18bc835220613a1409fa080f120c539f947fdfeabe4f2a16449a964098c614d6fc20e3e80d0403f0241f5d8a13147d749da88cd2a0d8266d4380e5070e0fcf87e2e312a3cdb01409afb8ce082549d88678d5f866e68a4d0281b41a6b539a157e18a18aa47a9d801ba82c15b4285ed10e2b52a839fd1fb5a55f8de22855f4af96518bcaccc2dbce494a296ebe26360d30cd1d486f49978b029766d49825f3c54d91bd255e22e279a69caf0742070495855195395e1742793b60d1378c09dc9c7053216512cbd2452209ec4be4071c5a521b696e975b1060278c4f77f59bb6ef505a7223d5245c2faccf2d7a7b17bc6a08396d8036223bd5f0ae7ed2f2891a3c4e8846b79bd36ade7b09fd6627114aa182ae1731eb6b9cf19b8b841c3c2b6d8056888915d6f3833646d375991e22759e6ca02fd6241ed37565eeabe5d1c1ed7734680293580c5485cae983d60911a42e17e7646d7d5bf1b3633d1bf857ea6d0d6dd6ac3947070d93be4e391a921af9366211aa3384aeeca7a871208ff44293b9bc62fc7a31c346b3bc0a41d8ec2faba055c65299d4680c399879ad3b09b91ca75c154614e7c2365c05d9e1e1bf0312970f67ebf20490f3b18aa7c84bfbf44044a62091158a689ffb7eff443a0d006f17dce991bb9008594904f9599b39252e57e6a7ab50656c9e5a8df72b58d51b7c9f4435e6f52a478dad1fa0f66a4117c91477773fef118f1a5b2e2246a83126b004cc496b97eaeb0939d7fe1fe9df4c45268c13f7acd87f8d7a7251397537066d564a275044ff3e9013c7416550403fd3b89f31931a1c9bc4c78890d6039319c12ba5bf473841715e3058c08b58f6c885980ccb7196c78fbab8bfc448f50b23e59de65950bb9e7523d7c99364b96ea7e2b23207d02a56f9a6ba47ffe607b0110784790f007d11c1a16dfc27e06e1ca443bf4740ab87fe60bd2e4719005c8706dac42041fbc232faedc6e10c6a6f75b3fbc206b8f7c1648c1104fa3deecec91ef7ed44600c3779a8af114f9e25af821788f45bd741c89b36e53c6e5e6e983e23f372c640298eb2ca3865a651b2add2714020d6de218a78a66e53d5eb9c037921c9f13da3795f03f8a780a09119c28b795e6e6b97d67250bc77c40a81ca7253285c8d29c99aecf5b4aec5316f45c7934a7c13e79ba5f526153174eac6f0fa7f1d5423429dd2204d5a0749d4f1d72ded1463077de54e2914170e854d559814782791a449f5831160a02643aab224d9ccadbd7c12366cbe5895233711003e08701b69ae17495eb9c96cb4741fa95d3a2342926bdd5a796de050b055178f9e91ba2582ad1dda122e9168349f910fba42f2389be4035b6a4a0776ec380eab4a7e1818da3933777a20728cd32e8aeb3f6dc1c954b5e7ee588996ffb14ff769561b1427200b52b53eb0c2aeeed7d7ddea931eb9dcd23c5652b768cf3fbefce53500b8030754c5c1ca1108f3ed46afb942bfc01b032b26a8ba44e62c0365ca4033b1b219a80f8a2ca3ff9ad952bbfb2493969d7eaa90cc4aadbcbf36dc76a0c848bbed2978369892c56259b660b0a07ee28fba8b592f129d029bb31b80535d8003742403319cbb781f5e4270e2aac914ced8a3fbbaaa3d9f1e834bad9b9f07b44814681c3e7cf0859f312e121eee65169a441b1dadc576870842c917a36a744b4f4da91c33208fd45e9c462ef67ea2f432b555a46ded01dc8ac89fafc61142efa714650817710edcc68e99b475b7ab403a6de405f94656a27d39fe369e561b26a5457c2921ad7d3ef554ad5330d553e322f95d5c40c50efce4a009493e71875eb2182d0f82765a780330127718248a51508491c0d2bd80766fc4007dc818073c337f882d78d25f805d23f12d1db526fca5ffb5be0e54a6ec25e46bbda65b6bf5aec648184761fa18ff033b31c97d42fd3a43ff3f7ecaea4d304d336e0e917a0d2e4425050be6d7fb830cf4562eb87b213a5d6a0dfe7b3d63f8e11e3b1efa9411183811071ad9e3d533211eb93739574879ef787af1314cbf7fb4e874c3ce87ebf8c3f60f93f0b8ef56a6f27811b193c26971d3e773e76e68c5daf56140ba20f67be6242773bd78452b12738a0ec3b6204e93c9377acc0358971d74ac9bfeaf9245d9421a7d20b5a4a2ecd40e4d23d71ca5a7e131b932acc89bfbd6866b38719ce78d212cf028cef1628129d642240f4b2730357494ac27446f5e56c0978e0c2fc239ee112666f0bee00ed47f0164d22a5215d5db82f3ab6cf249bb50703d3dc27d71423028a4f72123edc079901ef7ccb56bef61f6591b0477b23cb07795773f20e2bb12e5e0ef459fea65851cfcc4b387fc42cd5e71bc089a7f4381c913855a4f2f1f0106cae843799f89fa4b279a364f633a37e4b2640c69857e61279dd803ec5c6d039606c9c10584fac7c1d5b10de7b25738d89fc5569329afd79e885fdb953cad2cc05964e713e4c71e4c07ff1cd10c406ff55cbd851532a4839738d7227ed257241f748badd25148e8ca89366948ebeaee84d613987f693e0d213aaa594db3021fe702e566a8ad2d75ae4f812859398669a51eabc475fa34a0d77bb9611932a904ba84084825294979e0ede81171919e8bbab7354cd01123a8d61d9548b311ce05d765d5f5322a49e084b9796079b3196f5a5ef6761db7007dac93649c4a387d02a34ad68acf136903543014ec4b3d4a3fe900211ea22ea18eae5100b319d329034f88cb7f95f41dded0a0ad75b68c71278a61b51c19c035158fbb8259d36bd505735e825c6b8f845e8845155cb93ba122d3e98c3db8da44666609cf7192ff0d15d728de03a78427d9b166b5ad8555e4b65afb3244bd53993eb496c5cd50833d22341df3b6674d72bb8271c4b82e7f03740f15b18a7ce3fc8943f81f826fc8fd227bf5ff41b0989a237271e140ec6ba99a0c2c913033c1dfa00f9613114733a42111810a91eebbb5bea13246518438196d97dabf006219b2203cbaf73568d7f8077e01b07756ea1d188ab0a2d7d2b901634618048e281da916256acd484950b7175c097adab59b620eb4718fc51493f0566c9e30124ed27baae31fcb691fce092acd8a3e8ea251794afe8d35d55ad7037ee89476c111e04610f219a1aee124c22ac4339cb28af586a14d986b328dcbc25636df3bd150a77cea7298745cb7fd6b2ef63e7413dcaf3505f98d6569f1e4c8f3d5082903c2a15cacd73e75fd8df54e2307f9e71217ebbcf22bc61abcb8690a22409214cd7847da52e761f7ced049a31c763c92153a0b2904739905c9cb26452f4f573ea8f9a7740b72e854a50ec596115078a8b11c57010a27a043652ff97f51ca448f30614810e3765b8a4149a63e291d1683a4b9a0b964f6ffece7dfb0cdf1c4ce4f0fc04be99672261f50284503148cb9351ff4662a49ec4803560cfbc9165ad379fc90ce53beb5ab320807a8d91d0a10934a2b9e4363934b89c786d314b1a242e9128fccfa473dd4fa8f8496710aa112f31617661961f5e97fd2c3fea061bbfb54946e7687ec43dec8861421bc140c7c1bdb0a3d9b5909cab572d3434a2431c3236a57baefb073fd1bc6d6d1912532702a84570676a2eb0d0047ac4b2bc9fbe5c927e3dceb3c5c249d3cadcbe929008bdca758870e554bc839114d0ba80cca1fca48d28166f949e52c54e6928b1b9605ffb5f5f6b3db23931f7e59b994d47f7c474a3564aeb179664ee01f91770839111d61a992bd1ce5fe02631dddf3d4ad212e79a3aa20c635f97ea8d6db1a1bcf6ec685335f5a88413279438d62e691fc5848efb92ad7dbf44ea4e6f23a8d20414139bc656a727e0ebe69c8a9dc3802d314028e5a8643622b705ae5ae82e01e2578c9d36a252ed8138a7aacac14849121aaba10a66d6ddde63431e9bae2151950ccdf9487c6874ba926706e38df897c4ca3183893c211c3846182cd2588da2d398a58df3e979a559c498fe06a4a7a0163ee286354b8da07da8b9eef9945c618800d7ddab549c895cc7e2ff3000f1781554884bfeecb44e4124d603ebc19b829730a60168980a8fa287fc8f86c0a8a90ab8f38e71f804bba01d8365d1255930ccb79be29beba86407961ab765e979b3dafc32bb1f393d16702fbc3566aedda089832173ca5a7e5069fad1e0660959d49e0bfefd74d2314b393f2fcccaf682a22bea10fe8179d3ba752e8044853c47f4683b4310914f61e58b7a1df42504a74113e6facddec9a9ae5b220a1d6cf4fc0415e9f725e11104af98a7018d5dbf4b5132e8a07a7b4803847e32293d13af6a33d297cf15e40db4bade0a4774ac86b0b12fbacf70560a28f3f7b4db8fa73364a46bb2eb57964005bd8e58c2e61eefb861e701eadc30caea24196fdfaff46b16dc10ecaea90bfdcda36bd80ec0f006d9d2e1bcc89a79ee552a9d1d32fa60bc21ea126a76871ff91f479eebbf887884f360274da76723b26de1d7a5e8ce9ee53c7bfe8b5f29ec186a407e75ca0e3d661862b867f5fe4b79871eed93603d868ecbefd6a9b1188007d91ab5aa9944fde3f6b18da6194ed89cde701078d634b6c54192dd528fa637977e5c3675f4ada2b502e13be091100049adcf52752f514bf95c3fe99e4e20abd509446b5fc66a8857e1d2001066787b655dfe8931150d5648b382f60bbe72d6e66c73e3e6529d54dcf4811cdcc0314c615042bc66bf5173485c9881e9544493687da1731b6293a9679fb04b9622e9b578c81c7608e01f3053cef3f632d4ef5a58f172e8bf12259f6d5f21e25a74e2fa134a379f6308d57c51335552484fb0e88867c9d243bc181a090cb031f58e0fa9f655929adfa77a05baa4894c7de6a6d37290d3eb16283ff637dbd2fdb3500df9f2418aa9a9f250fde66f091fb6ef5acdb61a27902e22fa6d5fdf0057d28171834d980f73ca8ccbc0b303a5062a068e03e7ecfea78e72f78bf920d00be36994a8ee4f42a36ed3a223fba9826a967f946e75760a85a595015b8af2e24ad7c59e99b87f60a5fc7c76e351614576c43732e32d314ddbb85daa881eeed1b150a822d9945663292ce60728bbac00dc5ba3c649e814005a52df5c0fc59b81011cfe6bed95a7ff1808bf7c6e1984dcfba5975ca1768a00381f2974a32d1151e75c2894ff4581c65de16e354ddf938c056c93a50c61579815a9f0282488c76145951a635f929a71f8e662224c9364e12748d715cbc7bc93c5606f07687844222416ad582df9b9091bb790236a28d090291602b43b80c146036ae1d2d3c911123f37a291933fab7e30deea52324ab9ab3adb486903c9960992b5ad8cf6d89670e608c9b4061890a793116ccb7fbce38857e020824d1db42f14fc651164d22a60aaa8f2ad0f20b63778277b1f641d7fc5d12dae47ddfcb2d55e6b870d8b2814c20ffc73af5fadee89dc3b3cd116e881d5c768520e9fca681290ab5c58ead371022b65ed2116f353e440fdbe5d2fdf3f025e2a6d2e9d3e94c9293ceca8086b0de285a7305750eb30cacef7b5469a0797e42080970c0c3e70f990892b6ebff1044023dd8d11b77a419b3aa19d4bdbfabaac8d553dc042fe6c4555b6a14fd64ec980146ddd60ce32d76980b01b71eedf0bdee8b18f198cc0255d73108f17b509bc13025bafa936bc61579f8c1a9930017d1f0b042c5bb0566743b6c731e05e3bd7d1cef12b035210c3ecf81d2caaa99da32fea59317e383b970e419f861ae2573fd2b3c6b4f0c1ca9dcd2203e4b1be938e7838f970bbbfe78707468e381083605301c190b6c5f06802b1dc8b2280acaab86ac5046231ef1060033cc35ee76fe2baf2d661ab0c19ad8987ab3cbb89c97f3c6368ff099e09d1febe8e7f6ecc1479f972b5c27cc320c875b5f129f3b6ff24b4c8a2eb82af590659ba225f3e190fa4e6b1008897acc6e797f4357055c26eeec55360f23d1da9543b6ca7bb34519652626273e877efaf30df25e18578770476876265c499564d49f34e9f6a51e30b1dabe0c32c7acc142ddde363e9e60696c0fab4def3165b7cf5799816352fd5147cbbd053810b943836eae47e715380acaafecdaa5c56f78ad25cd6beb48b30b71e97224546462e575474e305a384c11241bfb4922692f7ada05ae8ffe3eb5a61f4a432346ea3071cda58fee452fbc4db70cd573ef7d17106659050ca1418bbd2681da0af56fd4cc9e2c6418372609222d7387c272a336e2b34a001899e647750744331b117ae4529b0938e4e4712dc5686b6caa250480220d3e94a35b9632e3a73fcd126572f8c3d4ca698b890cd2cec074e5e7a8a7f6f624f385cd36b9d21144013e1d4d49e7dadd46d3bb09ef8d4358a93c905259d943a18b6a020e9701b77274ef12ca1ec8b6d0d112d3b1dd49f29c7bc13ee5e7ce00c1e5ecce92a8c5b0b5b464094dae99c3561ef02693568d05c04104963444dbfbe0e6cd804ab19753689d2f1254bc80d6a1dde41cae49945131b625cbd58bdcf37f4cf699f10aaa7267e1d62acaedd0bb43e6003a32c7112ec16756df55e6b945e730898cfcbaddc25621cb0f3ea6045ecd0c18e3c46ceeee924bcd8b5931869ec38afe8b79b6dcec6e649f26fe612ab89de39ad9fb9d355f67b6608ea70697d9f7125cfa4831a71b894a0a0bf5eaba34f2e38a421e11ed8e30af9a02bac691e911dfd2c5cc8bab6fdcb23904e79827c342bca3644d0ff0be3bb71ebeddb24e04066ab1b9fbb82705a3266a1b742992abbea7a4b968075d5348ff4e1068b01de9997679713f13c50b558d69224e93ff28adec2874a0f0131ed5937a9a84f1a1d27815ef6d4412936fb2d9ab2c9f461e5eb9aaf0b937abf2c25c62926c3052eebbc13a311af28dc949296b271595a279367d738f840aab4617ae5d46cf23b6a6f6b932ffc79d71daf27bccb2128f76718008bc6be9dcdb3c0b25b5172b1da3752ebfd6f9ea2c8c609d906b690b01c120df01729bd22d73b94dd8afa3b34ecbe74d60ea6d449c60645f1528622ff8a547dac1025fa5fce863dddc98f0b30fad91b413d57072fafb52119e58ca1516eee808b2a74efc1cb43944b1974e17032d21093d286720396ff7399fb63c1f79cd6a75644f67501af7e6d876bc562174d7fe4eb0434c4cfee35ac6be75ac1820791537c5df12a815ba14d92b0f4fd6d78169fa6451cfff0eac6ffe080c5c2bd37fc059beb2f3793a7cff7025761e181b81ddd54b11acb6032db51a7a8ed713315f3711ea9f0a97830ae8b36494b8543ac9b862745c4215db849ec6a8fc6a1c5ee285c997e54b3c9fa212c4eef18b07de6901258f3ccd0dcf9ae10e0cec6028fc22b8bdd77d9f5f5753eb121e13d18332b7ab5e4e1b8da6ed82e9149a8261093fe0c9ae8dea8f167327c5614e10282a690196332af402f400d795d62b79112c1b39336e2d239a9a003fd684b32c1eabe3d12736033faf849ae7b6d13f44770823917fd825b94292e2cc911cc0bd9f55e29597fc6463429b0e3113ce09bf1223150eff80c462c613876c1afb7506910c9d57bcc4294dbf2154e84ded32aee02db67e7e6d6ef0b310397c49c631a159f637d7b54fbba46993bde91e06229569289dd10aabdca78e37e5e65111201ba9db43a5d736d09002d9bb979ba354884f0ddc0e655817c6eca1bf4e8399e6059ce13bae13442905698486b29df39028c226fa402d682da8022ed12079080cfe05a60c2764aa1d7b8ce819ef89b73474a62a32750b401f56656c401f6fbefb83b3fa391cbd494326e63cc172aac33ea37c2ab915cfa8935f6c1b643657122c82828d974eb995919f27f9045f1bdaed542120f563c340c7534d63bd89254d5aeb0962d72358fddf008461ffb28c7ca3331224a274f4a23db46311983cebb2a0c25c643781c581dd1853a98484e7daa303272696b4c3fc3250899de7b73273f0cb8271959eeba9f47d6f27845142bb5a30241b037f5bf9b9a97dc46a8293e61834ce50f783911f3be4c332cebb2a1254d2d02770490657b1d03830ed3ee8deb26801236387b361c6422b4119a60b45c17936281de0179aec58b237faadaec4581354ac0fe01e42e4aa3e7d9660abff18d87106ec366b781a8e7b6815aceeebca6d1742fa5b320e1bbccd556fcab55f5df0fa25613aa330b14f831113d7b0e58ce43bf10ffbb5922d8037fd6e8895bec323a41dfc55b695ae69ae883e77735dc097ad451c8943fcdb85658d42a356aa45a10d079f8b8125a7b007f583ba170dbace2b0baeb316a85c9992f4cbe7463bf97e420595f7e9889b31842f7bcb87ee9e1659e67146d3eb1738237d580ec4057b010007e11de07d72ef853d011846238a94be1d45b6af5221dbbaff5d7f2b345aac312ee1f0ba76c97dc811aaf550748da69a8e703e30d162f6c4d5e3bfb584709c610de2f1e1324166ce349aae1e7925db177f435d4dea9943c13a1a14583bc4806811ec85e76d455b6ecfe19375719b71801e53989ffd31d68393958d5db8d28f5e23b717a3076b3f25ae79b373b4c2f8b74ea54e68d5b611508ca732b86b8e682d0d2fcf22fb341c462a809a437c34275a2b1e99cd2f306ca0e5c471513ca2636b189a2f7b2fd610eef2f1f564c494ccf64b450345ad58e46520438d478c922304080088b7ac282721cda9cfa89dfda2df6b663c9e071514b42a6e3e3d3f4b8a382289a0b58bdcc4d9e19fa811d6cf64b4a703bd072cd660021d4d515e59e6df6b3046f12a2efae22d170874e2650dcef1a24d6974f77cecf09d6fbfd8b34feeb40652708e4d2e7170b5938e06b95d7239537f7ff192bded21492eb0b817d58b4d165b06c7bf692e0321aa3778a74dcadc77994701581806ecddaadf49f7104e7f0bc5921a00718a37a6af9d199e6e7d94a2bf032e92c0eb564adf2a7fc68f24701231b482e813783b1f35ada11c23433530cc7bb6fb050f5e9ecd90b18268a629098693da7c2767302986f142e06ccea3467126f980182235dd26f84f3b4168402ab0ffa139de9f700d94fded6ef3da03e6137e64131ab66dbefb912545cfd9ec2140d43b2b04494a1cb5de412225c593576471f8421345c6461404437cd50bdc05e52284204951c094ac5183937c52eb1f89d5efc2ddad671cfaee747cbe411027d28a630695a70bd84e6c8f2204baa7816303b598333816bd2e069cdf2d900c25394ac8abc092015c03de91897529f88364c5920c96d79d36adaa49fa4ee79a1acf5eb5cc8c68c2d207e2c00a4be93e9f7f68fbcf3b22641cc46af5b00e468b266253abf616d8a7219eebf4ed0c186804c9aa518e6498a95d27870b09e9a7910d7d2897abb953838358a1c632c883fdf1da7f014a0145b925a4d62389afc2d21d320b72198c16c59816aa7980f35282f8161997479712acfcbf2ae7fc939048221d36c6e5386de18e2e31bc9151bf31caf26bedd9d9a294b1582a0ee1fd54da2b29ef6dbc542e0a402324832ab4a568d02661bee1222aca13a8b8fedf6d1a212c033e3b768116702bbd085431c7df39c570843069c428878590357edc235a0d12653312e04bbf99f98c84e15b59ad2dc98ebfbb620ea25d8800361638225f0f26fa394aef30b30116a967ec0c6a1fd541f82a8091afda6a6d6efeb080c1e0ad26577e4b645ddd36cb9658dae88d305d1fdc55f0015d1b457fa072d4873e8fdb1e81db21d53d8c4cd8c3d14ce58e0a65f6e4f36053c3c0c3243d19046bd8adb80790a527dbc5ede9d8ecdf60de807d0e924b52c138cd1ac03a04d8e88d07c6de29ec091c14d2da49d5d8782121da331473e86bf1bfe45605de793e288bb1aa71066aab84a29256bd7b881523d47ecc983a2aa4255097c5898bb5af93edfa26d5015bb0c6cd798907d7713346c22b48aee272d38248ae714ea7ec5b82561ad2311fd201458f45a4dd86e7a0f108886af9c8d7613617ef0163f18e25aa9ecb792929c4b0889000b9a49cbc9fb16aa12d3cfc54184f17e50144038d3a2a3626f31e2d9fbc165df3f9fcd75d9e99f535022ec91b19bf7c24a033acc8944dbe79ccf224ce84f0ab04273fd6974069d53931a3a69ba7714be5da0f234df46855e054e8299b8a573817dda6b09843956df7a0e2e21d4ff9d91853431e11061482a315f001d434871e69df4e1f24275172d544c130a1c6d40b3bd6b1d7a3d4520217577129eb8a792342940a31d20af3a99f0f4e005da9e561e5237598bed6ee3ee3820a170ba23dcdec74b1b3b461944639305c1596d3b64c83582a758a345624f51be201d7688c69fddc0ff0eceebb2de6d06dfd6cbb7047857b1ff42f680eccd1ac212b73a863059add2efe748ac8971207921c1195f099f9ab6eb43dd55a303e7cf52525392301fc899dfbe7408424a67c509243e5eccd4cfeeeb00b78288506331f06eaa5fd76a09b44f56feaf2d7af4d23b05308b70ccd0c1d5ad8ad1d47b6ed200fc31fa90f5a3102d27b3b1245948561a7259fef448d208a4d87472bb58b647024fa6e040e10f43ebfa76dff924cd8eb7b20dd80bd5e8cf3253fc75ba2921ba21a0ec8828f4e0b8b951041f4e6b816ba41c45a187c8a6434614b24a67aad33d81226a98970af6c1c254981f8fd1d3325244b579ba13038c2e15e670049d7add31e5883b5055d27d3764f707a699a7c34dedbdc2dfa2388b865e81f2f476a9d74033cf6829ede66868d1df503a99b0ab65dcc9ff4581d674e3cc0edd62f6144551a124eabc55b48029f4d416c598c41215d2e3517fc86598aee3d3876d356fea301e68d91f485409a6939dd9136e8ec513ec47d57cfb3f3e0c130160725b15d712c4d57fb83fce70a48b1f42a06f7652f13a403c42e096aeffd06aa22ababfa6f16f60d110b6e2b66ea8828bff298724e5b5fcf493b2106050b85f98ed06491ac17b4c24d508d47a33aeff7a156692b0f923db58fae1b5cc154be223e28cf7085008215ea995ef6c42fc4c9fbd66a6bcb70b2b38709054ea35cd475bb49de59e10956147508ba2e4fb0382e90998b369f03ec4b14d8d41369fbac5544b4139cbc2bd1cb184c504f95bd229f7df496b1e8148eab68ff7a47356fcb4370c00f9a452fad6ce685e37ef2fbe60c09063c8269b6f4a4f58501bcdd6516d59ff13bc307194265fc4a499426ae6b3d0dbbd38289a438005d3a1230768fe7f22a6591d73c1897db709e4dddee2ffea8f9cf5f7505c38eee0e056bb84b077f668fa3fe29441212919a9819f2e3e22e177096168d5e245ef96e5c116cf5c79748d55620b9901ef44b6da68ac3effcce28cf482c7117c35f4bef183e4bbbc96512a7fdbfe0381833ea263ebec528a88e53d624614b2be689b4755ffb1d27f661b4f5a41301b90dbebf58ff3eb42dbd35814ea7a002d1e718e722d9adad207374586887a5224a2e1c83282cc07d6cd0aa8d1c5758ba79462ddc323bc3bbb930373ef1c53b92ed2d96d4d238155532d1953410af79f3d08aa233e2fb256778a04dbdd925e7bdbc182a8858a15071189ed105aa6a222a7a4ffccd9aed35c4d7f7ba647273e2393025bcc927bc2fd289d1e434e8d15d038ecef09f44680b726df17fe0aab28e386d24fa7843e6fa06f472c82dfaca32d85fd582afd60ad817bfb11b3cae843c751e02776d66f8bf51a55acfa283bc6442d185512af1be7154b7a2da03abbd4a60c4127af50b915ec98859d5c684edf56b08cfe66418f2a5889f9b3a87aef425f6950fe98c14c6fe622d1d1c2bc47cfea2b721e97c8dbb1d9cb37577cd019545b71f0285121e51f77094251805dbd8d085ecfe8ccea6723dc062bb3fedf7c50593b0205922a540423bc8c3d5e42569bb2e7247ea76f0336bcbccbd1186dc9ce7482d03da45465c43bd0de56532970355c7ef777b177c7fe6a7532f317a18f136fde2224e443ab209e43f772d8074fdfea3b067c5381c65bb75025ee3f9dee77ce4dc03c743c2c27d44ef52d099e299389080c52600286711e9e1c734dcdebff96c9219ba0fc80d6ea186e6c29dc5c3191ebcddb75f9a6fa142abb71b0eb24ea3fb870d74ad181209cf52fb62d6a0b7c45d9f539812c01d85dee2e3de2131e9fc0e9e2df8e785877d96f7453353cd8e66536472753c38f34c924a11f9bcd3e1df5b24225211c57d2be13d95cd10a32f573bd85de9b70bda007117fd932d6670a44a0206cebe7808618526d6bc0230034e66ce9f7e8f7696a54be414b4caa9cd0855400c5b7787ab19599dffb8d491cab2af3858179c4829812ccfa8950148db8b0925224e70668ec89f91317e477701835166eeac74a894791d8eb76b1baaa550f11216c0fd4163f586a4e16ea766eeeda37ace01ec98c628056398c52e154fb4fa782f2af6aceeebd78d7f15b25ad97c9903deecb86333fab6be68bd8c9ff9facfe4214f0826fa40d3f55a50d102bcdcfa14680605bf36c2d142623c49239b8c22c31977245235b6d40d8c19937dd89007a42e79bc006ca2dbd5457f812fa1f32d9f1029f5a1a4d325d1518f606530e4b5d112c7fe4fd991d78467c24a0487c346a3258eb48dbc8290f47244f0dfd8152f2c2b6c12d1825bd4ef6c35da4a958ad6db192114ee30ab3d73821882eb27c746bd064c8d29a5f970c77faa3b11299f4bde099787744f327baf3422124943d9662f27c114be514e8c81cf618c21a009e2f82331c0ce3f8705e19f585abb4fd7adf1b9718854ad54a8bff4281e2dbeea99271874da9862988c3799ddd9031aaf478420767989fb32ad7577fa16523d27518e0a961ab41d2f1a80097e95d8c03919749c46f05967906ae8cc3ea5de2d4f25d8110ada472dc029c62964550f9ce1372c049f582e33fad78b81872c25d5e7c0521a3f7c7a4873e16b9def6fc5cb6b8bd7d918e64498de20f4de19f6d0c344e526175ab4d28c8c1f400c5b7b8cc871003b1d5cdcd40bb673a700a1edf8281ebc4a0e3721c6515c27adbc7da9925914ebab3eb9dae0cf2200cfbde8e446c38d7bb4295bcad28c370cbdd42eaac458f3ebe5cb7fa2ef98f984babcbfdf06f34b8f65091ee205e13701a1c2ea8a843f5c4b6efd809fcfabead21b10bde2d146aee526215a4fe1159a4d459d3e0016eaf546adab9cdf32bc4379606278bade85a725f7158a13d111c9446a19f6b0bce14bf1651b10bc0123a2a09faaf6919b112dddaa0a0ea3a4ecce723e76ff8831e4cd13d7424c4be81605104116740d58fb086a712e326db739292746d1985c1f9ae8899358e00d533a75efb5d7de1ad2742836322d49011ef90a17d991fcea02ac3274e6b2a9d8ac45e7227708a6ff1b1434586a11db76e8211ddca3020b2850fb31e09acc20b25a225d945840db8f189168aaed18b1ffa314189d1a899b0bde3931abf4178309c5e553cd375db69e22ba0131d8234633d63ff52a14b8ee82edebff56b20612aaa1828f1836d47cd48a889358a31c2ed3975a00706170f68e5c4fcf468d90ef43d57ea3f28b737ead30045fc1be2f085a00fca043219f011f86030f7f33ac46e1f9796a788f6b1c600c0cd7f74d21781ea6582c1001799f2d348f46f6a81f723560d7e920c1c8fcbda35d6dd4560fbcc1e3fe95963b4b591935b7506b55e309523f6ef0372aa53f6f6f39a8fced3033a3b0ff7bf32b9347d397c052d1114065d98e7a107ca39c1ebb71d52d79f101b2e9d8bfa85029b731263a28b39c9f0e9400d3dc6dc1c9328a022a74b704f2000bf3cb1c7232b14fed08b9c620894f038b79fa0dcd99081b21527561ef27b71531fbc148df29328c03a958ae4a00e803eac930ffcffab30209fff34b7d3f79102a1b45d43dee8dcff85bda9102cb809b22e2e10f32f6af247f3cd2b5db032c29d5334d06da44474e388dba32fef58f6f41b64ac8c0ec6cfcd4dc3e0f2673f92e5b3ff63c00f9d5296e18b42d1abd074dac590e7c6e16d339e714a8168fe9dc14582423af2f47b9078fea487c501377206684605bcb5724e6dc63c1e7822abb0855c33f180b5b20ebb2b40c27267d315c4c2bc75dd5cf4a2a5bceadea37ed78153883fb91a526e6f2a716b7139fe0b86b2a8900372f0172f3aaf9ee4a2019ff06e9f15dcfbba8dbb9dd9f3a28e9f54bfd43de7e0f8482a383ed5b608df6cbb6107cbdb223a442c91a27b79c4bebfab136990c609a006f94a4b4aa357bf3b4d70581cb1e022e7beb4fec5b5fb12d0c964ae273eb8ab4dbc0ca9e38768d621b831eb161ad789abe47c91993aafa4250b09fbac7fcb9f489d93a0be0cc4980ac561a170026532a309839237bfa1081eae9a778f6b16665de3237f00c8124fa1fd3253636290e6dbe6eeae74358d67aec6b012104749f53bd345b362b75ab3b549d088599bea97a0c7955ee2b01338bda7d05c5579e6822b583147e1d0958e70eee1b1d6559c7b4a21947d81d6d7f82d511133b9ce051588ce4a1a3e381ab5775ad0a93dbe6e14e6f4d040a4aacb7864a1468b61fde0d2aad5d57f75c6f9679afe9", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed00000000000000000000000000000000d50b7cdf578f4412a219947e71659c0f00000000000000000000000000000000585506e2fc08284102cd3ce553aa726a29012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f17b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } + }, "test_exit_code": { "crisp": 0, "folded_export": 0, From 644f3c12a6ba7ccc095c8bbb6d89158c905cc5c1 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 10:06:46 +0200 Subject: [PATCH 11/54] fix verifier pub in with dyn H and update benches --- .../results_insecure/crisp_verify_gas.json | 6 +- .../verifiers/bfv/BfvDecryptionVerifier.sol | 6 + .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1635 ++++++++++++----- scripts/build-circuits.ts | 4 + 5 files changed, 2359 insertions(+), 927 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index d239014d6..941915f4c 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -17,9 +17,9 @@ }, "calldata_gas": { "dkg": { - "proof": 170124, - "public_inputs": 6132, - "total": 176256 + "proof": 169908, + "public_inputs": 6144, + "total": 176052 }, "dec": { "proof": 169848, diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index 045da86af..9654d15d6 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -51,6 +51,12 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { /// @dev `publicInputs` index for `committee_hash_lo`. uint256 internal constant COMMITTEE_HASH_LO_IDX = 3; + /// @notice BFV threshold `T`; must match the compiled DecryptionAggregator circuit. + uint256 public immutable threshold; + + /// @dev `4 + DEC_RETURN_PREFIX_LEN + DEC_RETURN_COLUMN_COUNT*(T+1) + MESSAGE_COEFFS_COUNT`. + uint256 internal immutable expectedPublicInputsLen; + /// @notice Underlying Honk verifier for the DecryptionAggregator circuit. ICircuitVerifier public immutable circuitVerifier; diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index d30ad9e01..110829355 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,122 +10,238 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), publicInputsSize: uint256(127), - ql: Honk.G1Point({ - x: uint256(0x21b909972bfe373b93f74e8d1b23d4f9da2d359033f9bea3bfd0dff4b14583b7), - y: uint256(0x00cca3f61b1c83f29f138790d10246931d7ac90c67f0868928a8f87f115bae3f) + ql: Honk.G1Point({ + x: uint256( + 0x21b909972bfe373b93f74e8d1b23d4f9da2d359033f9bea3bfd0dff4b14583b7 + ), + y: uint256( + 0x00cca3f61b1c83f29f138790d10246931d7ac90c67f0868928a8f87f115bae3f + ) }), - qr: Honk.G1Point({ - x: uint256(0x282acc4814ca5bff02f14c4a58b751ecc70c1c01494610385bff82d9072bf548), - y: uint256(0x1d4bd882f21883ba61543964458224dbd4a53595d002e18c0db812390c45416c) + qr: Honk.G1Point({ + x: uint256( + 0x282acc4814ca5bff02f14c4a58b751ecc70c1c01494610385bff82d9072bf548 + ), + y: uint256( + 0x1d4bd882f21883ba61543964458224dbd4a53595d002e18c0db812390c45416c + ) }), - qo: Honk.G1Point({ - x: uint256(0x1dd7761ff2c72c1af984fb53d1b3b42460c3323214a2d7dde42c62973d34198e), - y: uint256(0x13040272a4c0bedcfdec2b313f9efe3ee9a0bbd5ca58d9432dc8d832127e99b0) + qo: Honk.G1Point({ + x: uint256( + 0x1dd7761ff2c72c1af984fb53d1b3b42460c3323214a2d7dde42c62973d34198e + ), + y: uint256( + 0x13040272a4c0bedcfdec2b313f9efe3ee9a0bbd5ca58d9432dc8d832127e99b0 + ) }), - q4: Honk.G1Point({ - x: uint256(0x174f2955dfa54bf68658a287d4658af6994ecd888993452c4c14f1c60871ce56), - y: uint256(0x0cda4ce133f1492a2f13a0e29c6df44b8e697f84e481f1b2dbf2d8d741be5e04) + q4: Honk.G1Point({ + x: uint256( + 0x174f2955dfa54bf68658a287d4658af6994ecd888993452c4c14f1c60871ce56 + ), + y: uint256( + 0x0cda4ce133f1492a2f13a0e29c6df44b8e697f84e481f1b2dbf2d8d741be5e04 + ) }), - qm: Honk.G1Point({ - x: uint256(0x22ac07bc4c7d102054ba8dc18954f43d66ed6c57ede3a78e5fe44e80ab26daff), - y: uint256(0x01a5cce0a2e3607ae4fc406e7379aed53d7cd2cdb0d3a14e759531cee30cb9ea) + qm: Honk.G1Point({ + x: uint256( + 0x22ac07bc4c7d102054ba8dc18954f43d66ed6c57ede3a78e5fe44e80ab26daff + ), + y: uint256( + 0x01a5cce0a2e3607ae4fc406e7379aed53d7cd2cdb0d3a14e759531cee30cb9ea + ) }), - qc: Honk.G1Point({ - x: uint256(0x11fee8c098df12a40892852407a771a7d280dbfbab5eeb06b23896095ca7a290), - y: uint256(0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b) + qc: Honk.G1Point({ + x: uint256( + 0x11fee8c098df12a40892852407a771a7d280dbfbab5eeb06b23896095ca7a290 + ), + y: uint256( + 0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b + ) }), - qLookup: Honk.G1Point({ - x: uint256(0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424), - y: uint256(0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409) + qLookup: Honk.G1Point({ + x: uint256( + 0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424 + ), + y: uint256( + 0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409 + ) }), - qArith: Honk.G1Point({ - x: uint256(0x2624d4d9d7eac2515cb4b322afc263ddc87c535791e2206eef64bc24024968e1), - y: uint256(0x105a6a9c813245babf469aebeafca60e878d41b05f79125dedf362bee561b5ec) + qArith: Honk.G1Point({ + x: uint256( + 0x2624d4d9d7eac2515cb4b322afc263ddc87c535791e2206eef64bc24024968e1 + ), + y: uint256( + 0x105a6a9c813245babf469aebeafca60e878d41b05f79125dedf362bee561b5ec + ) }), - qDeltaRange: Honk.G1Point({ - x: uint256(0x1cec49a84cd964f7dccf24f37f746eb4660ffa446ba4e79d04582d86fc5fb2be), - y: uint256(0x16acc276874333a56f75e2c79d9e723e9ac1bb18d1ab5bd579a3ab1702464ed0) + qDeltaRange: Honk.G1Point({ + x: uint256( + 0x1cec49a84cd964f7dccf24f37f746eb4660ffa446ba4e79d04582d86fc5fb2be + ), + y: uint256( + 0x16acc276874333a56f75e2c79d9e723e9ac1bb18d1ab5bd579a3ab1702464ed0 + ) }), - qElliptic: Honk.G1Point({ - x: uint256(0x006554df9837516dfb90ce208134e4b81d29ebf81032b08330501733f5f20d6a), - y: uint256(0x0ff31f52484554b3123ffc5c911d928e91ee373db03b305bd1350ae27aba4169) + qElliptic: Honk.G1Point({ + x: uint256( + 0x006554df9837516dfb90ce208134e4b81d29ebf81032b08330501733f5f20d6a + ), + y: uint256( + 0x0ff31f52484554b3123ffc5c911d928e91ee373db03b305bd1350ae27aba4169 + ) }), - qMemory: Honk.G1Point({ - x: uint256(0x28fad415a8ba66a6c2d15321977f696a033b56580937a63c0be78be9ccdbf00a), - y: uint256(0x229fa12d35300e25b3095908acfed5751d51e93cd6ecf4af6757ba5a4c540c18) + qMemory: Honk.G1Point({ + x: uint256( + 0x28fad415a8ba66a6c2d15321977f696a033b56580937a63c0be78be9ccdbf00a + ), + y: uint256( + 0x229fa12d35300e25b3095908acfed5751d51e93cd6ecf4af6757ba5a4c540c18 + ) }), - qNnf: Honk.G1Point({ - x: uint256(0x27769c90ab027f74a7f86fbe3a1832e41518cd4975e8ba110311664df43f0ce3), - y: uint256(0x24a8f977133bcb034382e4cbcd3e335373ae5aac0e67824a2647554a52536b87) + qNnf: Honk.G1Point({ + x: uint256( + 0x27769c90ab027f74a7f86fbe3a1832e41518cd4975e8ba110311664df43f0ce3 + ), + y: uint256( + 0x24a8f977133bcb034382e4cbcd3e335373ae5aac0e67824a2647554a52536b87 + ) }), - qPoseidon2External: Honk.G1Point({ - x: uint256(0x2eb3443efed96b06718b28d1bfbbc35a407b6af60f720ba5a9d0ac78501f0ed2), - y: uint256(0x17022aa4435561f83bdddcaa9174723a1e31c11d128a3455edc0b21bf22d334b) + qPoseidon2External: Honk.G1Point({ + x: uint256( + 0x2eb3443efed96b06718b28d1bfbbc35a407b6af60f720ba5a9d0ac78501f0ed2 + ), + y: uint256( + 0x17022aa4435561f83bdddcaa9174723a1e31c11d128a3455edc0b21bf22d334b + ) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x2ef9e66a814fe6821f53a2a2e1e93ac8630a347d7c9fee2afd2edcdc13bc0548), - y: uint256(0x0ebdcee17969483e898170e905ff58418ad7e99173fa87c028966bd8c040c923) + qPoseidon2Internal: Honk.G1Point({ + x: uint256( + 0x2ef9e66a814fe6821f53a2a2e1e93ac8630a347d7c9fee2afd2edcdc13bc0548 + ), + y: uint256( + 0x0ebdcee17969483e898170e905ff58418ad7e99173fa87c028966bd8c040c923 + ) }), - s1: Honk.G1Point({ - x: uint256(0x235a96328f656f5e8e3935de342e7ffb06d3400d2e11e03ff3f5e9729dec07de), - y: uint256(0x23b38403e02d9a93b48b7e21c10d7360fd7a39299f6aae2174d47e2df318775f) + s1: Honk.G1Point({ + x: uint256( + 0x235a96328f656f5e8e3935de342e7ffb06d3400d2e11e03ff3f5e9729dec07de + ), + y: uint256( + 0x23b38403e02d9a93b48b7e21c10d7360fd7a39299f6aae2174d47e2df318775f + ) }), - s2: Honk.G1Point({ - x: uint256(0x2c15698c01375d97f94676f1bbdb3ba5b157bacaadec11b12cf074cf212f6e12), - y: uint256(0x141ec5c8ba7190c9cb1fab4f19e817d9d8f9cb2c0cedd614d4f7820a2f7c4abb) + s2: Honk.G1Point({ + x: uint256( + 0x2c15698c01375d97f94676f1bbdb3ba5b157bacaadec11b12cf074cf212f6e12 + ), + y: uint256( + 0x141ec5c8ba7190c9cb1fab4f19e817d9d8f9cb2c0cedd614d4f7820a2f7c4abb + ) }), - s3: Honk.G1Point({ - x: uint256(0x22e7871e851cfc6514318d6f16d1c34305dd0e3c0dbe39df3527feda3b0d1eb0), - y: uint256(0x078c546da57b7d1340a5a5b11922ab15592a2c3d32553532c318f0a238768a01) + s3: Honk.G1Point({ + x: uint256( + 0x22e7871e851cfc6514318d6f16d1c34305dd0e3c0dbe39df3527feda3b0d1eb0 + ), + y: uint256( + 0x078c546da57b7d1340a5a5b11922ab15592a2c3d32553532c318f0a238768a01 + ) }), - s4: Honk.G1Point({ - x: uint256(0x17e47cca2b9876b87b90039176b89b889b2e6f6ab55bf5b6ade7026c1886a55b), - y: uint256(0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce) + s4: Honk.G1Point({ + x: uint256( + 0x17e47cca2b9876b87b90039176b89b889b2e6f6ab55bf5b6ade7026c1886a55b + ), + y: uint256( + 0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce + ) }), - t1: Honk.G1Point({ - x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), - y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) + t1: Honk.G1Point({ + x: uint256( + 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c + ), + y: uint256( + 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 + ) }), - t2: Honk.G1Point({ - x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), - y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) + t2: Honk.G1Point({ + x: uint256( + 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 + ), + y: uint256( + 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 + ) }), - t3: Honk.G1Point({ - x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), - y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) + t3: Honk.G1Point({ + x: uint256( + 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f + ), + y: uint256( + 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 + ) }), - t4: Honk.G1Point({ - x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), - y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) + t4: Honk.G1Point({ + x: uint256( + 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 + ), + y: uint256( + 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea + ) }), - id1: Honk.G1Point({ - x: uint256(0x162e6ffc2acbbe037aa8301684ed9e2d850a2c83a3c1a3164453b5c2187c8c75), - y: uint256(0x22f0647302fbfc4d83670140b7ec0cd606fd991bd3e7cebeee96ee3b6169e538) + id1: Honk.G1Point({ + x: uint256( + 0x162e6ffc2acbbe037aa8301684ed9e2d850a2c83a3c1a3164453b5c2187c8c75 + ), + y: uint256( + 0x22f0647302fbfc4d83670140b7ec0cd606fd991bd3e7cebeee96ee3b6169e538 + ) }), - id2: Honk.G1Point({ - x: uint256(0x1fc1d8dce21a638cd9695d5ed2d796b7b1423fe391391cbf0076dabcd5b1229c), - y: uint256(0x2e4d338298032c5426ca47e6970b8ef0b055728771a8ad6b455f4d3001abd402) + id2: Honk.G1Point({ + x: uint256( + 0x1fc1d8dce21a638cd9695d5ed2d796b7b1423fe391391cbf0076dabcd5b1229c + ), + y: uint256( + 0x2e4d338298032c5426ca47e6970b8ef0b055728771a8ad6b455f4d3001abd402 + ) }), - id3: Honk.G1Point({ - x: uint256(0x011c7ccc37d9abaf9dd6ffb88f045f8f6adb02dde453b8645b7a5461356255f8), - y: uint256(0x0186d6fa335ec0a6179c9edeb2cfca478103eb4989218cd11ddeb6a4762ff294) + id3: Honk.G1Point({ + x: uint256( + 0x011c7ccc37d9abaf9dd6ffb88f045f8f6adb02dde453b8645b7a5461356255f8 + ), + y: uint256( + 0x0186d6fa335ec0a6179c9edeb2cfca478103eb4989218cd11ddeb6a4762ff294 + ) }), - id4: Honk.G1Point({ - x: uint256(0x1be8e47ef6bff9941f3febe177d14f28448a16fe9dd81b1c9cfd05bd9136c02c), - y: uint256(0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0) + id4: Honk.G1Point({ + x: uint256( + 0x1be8e47ef6bff9941f3febe177d14f28448a16fe9dd81b1c9cfd05bd9136c02c + ), + y: uint256( + 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 + ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), - lagrangeLast: Honk.G1Point({ - x: uint256(0x201feccb28b5ddf7440c37e1a8d5676a8f9d7feb0e373436b3413fa9f775fd6b), - y: uint256(0x03f87d81d9e68bc20ce687e8a53620c9947d06fdc887f89e9fc6a023c8880e74) + lagrangeLast: Honk.G1Point({ + x: uint256( + 0x201feccb28b5ddf7440c37e1a8d5676a8f9d7feb0e373436b3413fa9f775fd6b + ), + y: uint256( + 0x03f87d81d9e68bc20ce687e8a53620c9947d06fdc887f89e9fc6a023c8880e74 + ) }) }); return vk; @@ -135,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -298,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -496,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -530,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -542,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -551,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -567,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -588,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -606,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -617,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -657,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -684,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -701,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -765,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -783,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -842,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -861,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -881,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -892,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -911,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -956,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1096,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1104,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1118,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1128,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1203,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1228,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1264,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1278,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1300,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1309,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1353,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1402,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1463,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1494,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1515,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1536,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1572,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1594,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1628,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1651,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1698,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1714,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1726,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1746,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1792,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1816,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1845,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1869,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1881,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1891,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -1911,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -1931,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -1955,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -1987,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2016,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2039,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2051,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2084,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2104,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2165,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2179,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2234,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2251,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2273,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2297,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2316,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2333,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2375,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2385,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2396,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2421,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2431,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2445,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 7dcc0571c..8f8d5f63f 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,122 +10,238 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), publicInputsSize: uint256(31), - ql: Honk.G1Point({ - x: uint256(0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7), - y: uint256(0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a) + ql: Honk.G1Point({ + x: uint256( + 0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7 + ), + y: uint256( + 0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a + ) }), - qr: Honk.G1Point({ - x: uint256(0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77), - y: uint256(0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94) + qr: Honk.G1Point({ + x: uint256( + 0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77 + ), + y: uint256( + 0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94 + ) }), - qo: Honk.G1Point({ - x: uint256(0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507), - y: uint256(0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda) + qo: Honk.G1Point({ + x: uint256( + 0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507 + ), + y: uint256( + 0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda + ) }), - q4: Honk.G1Point({ - x: uint256(0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9), - y: uint256(0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69) + q4: Honk.G1Point({ + x: uint256( + 0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9 + ), + y: uint256( + 0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69 + ) }), - qm: Honk.G1Point({ - x: uint256(0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7), - y: uint256(0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6) + qm: Honk.G1Point({ + x: uint256( + 0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7 + ), + y: uint256( + 0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6 + ) }), - qc: Honk.G1Point({ - x: uint256(0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb), - y: uint256(0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095) + qc: Honk.G1Point({ + x: uint256( + 0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb + ), + y: uint256( + 0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095 + ) }), - qLookup: Honk.G1Point({ - x: uint256(0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea), - y: uint256(0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1) + qLookup: Honk.G1Point({ + x: uint256( + 0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea + ), + y: uint256( + 0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1 + ) }), - qArith: Honk.G1Point({ - x: uint256(0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a), - y: uint256(0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61) + qArith: Honk.G1Point({ + x: uint256( + 0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a + ), + y: uint256( + 0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61 + ) }), - qDeltaRange: Honk.G1Point({ - x: uint256(0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20), - y: uint256(0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a) + qDeltaRange: Honk.G1Point({ + x: uint256( + 0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20 + ), + y: uint256( + 0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a + ) }), - qElliptic: Honk.G1Point({ - x: uint256(0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d), - y: uint256(0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170) + qElliptic: Honk.G1Point({ + x: uint256( + 0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d + ), + y: uint256( + 0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170 + ) }), - qMemory: Honk.G1Point({ - x: uint256(0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b), - y: uint256(0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53) + qMemory: Honk.G1Point({ + x: uint256( + 0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b + ), + y: uint256( + 0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53 + ) }), - qNnf: Honk.G1Point({ - x: uint256(0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c), - y: uint256(0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0) + qNnf: Honk.G1Point({ + x: uint256( + 0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c + ), + y: uint256( + 0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0 + ) }), - qPoseidon2External: Honk.G1Point({ - x: uint256(0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7), - y: uint256(0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce) + qPoseidon2External: Honk.G1Point({ + x: uint256( + 0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7 + ), + y: uint256( + 0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce + ) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351), - y: uint256(0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77) + qPoseidon2Internal: Honk.G1Point({ + x: uint256( + 0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351 + ), + y: uint256( + 0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77 + ) }), - s1: Honk.G1Point({ - x: uint256(0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189), - y: uint256(0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc) + s1: Honk.G1Point({ + x: uint256( + 0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189 + ), + y: uint256( + 0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc + ) }), - s2: Honk.G1Point({ - x: uint256(0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393), - y: uint256(0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c) + s2: Honk.G1Point({ + x: uint256( + 0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393 + ), + y: uint256( + 0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c + ) }), - s3: Honk.G1Point({ - x: uint256(0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537), - y: uint256(0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea) + s3: Honk.G1Point({ + x: uint256( + 0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537 + ), + y: uint256( + 0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea + ) }), - s4: Honk.G1Point({ - x: uint256(0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab), - y: uint256(0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92) + s4: Honk.G1Point({ + x: uint256( + 0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab + ), + y: uint256( + 0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92 + ) }), - t1: Honk.G1Point({ - x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), - y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) + t1: Honk.G1Point({ + x: uint256( + 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c + ), + y: uint256( + 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 + ) }), - t2: Honk.G1Point({ - x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), - y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) + t2: Honk.G1Point({ + x: uint256( + 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 + ), + y: uint256( + 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 + ) }), - t3: Honk.G1Point({ - x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), - y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) + t3: Honk.G1Point({ + x: uint256( + 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f + ), + y: uint256( + 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 + ) }), - t4: Honk.G1Point({ - x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), - y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) + t4: Honk.G1Point({ + x: uint256( + 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 + ), + y: uint256( + 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea + ) }), - id1: Honk.G1Point({ - x: uint256(0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9), - y: uint256(0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c) + id1: Honk.G1Point({ + x: uint256( + 0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9 + ), + y: uint256( + 0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c + ) }), - id2: Honk.G1Point({ - x: uint256(0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266), - y: uint256(0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883) + id2: Honk.G1Point({ + x: uint256( + 0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266 + ), + y: uint256( + 0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883 + ) }), - id3: Honk.G1Point({ - x: uint256(0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d), - y: uint256(0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5) + id3: Honk.G1Point({ + x: uint256( + 0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d + ), + y: uint256( + 0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5 + ) }), - id4: Honk.G1Point({ - x: uint256(0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a), - y: uint256(0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02) + id4: Honk.G1Point({ + x: uint256( + 0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a + ), + y: uint256( + 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 + ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), - lagrangeLast: Honk.G1Point({ - x: uint256(0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400), - y: uint256(0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1) + lagrangeLast: Honk.G1Point({ + x: uint256( + 0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400 + ), + y: uint256( + 0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1 + ) }) }); return vk; @@ -135,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -298,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -496,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -530,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -542,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -551,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -567,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -588,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -606,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -617,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -657,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -684,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -701,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -765,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -783,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -842,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -861,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -881,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -892,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -911,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -956,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1096,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1104,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1118,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1128,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1203,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1228,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1264,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1278,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1300,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1309,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1353,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1402,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1463,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1494,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1515,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1536,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1572,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1594,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1628,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1651,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1698,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1714,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1726,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1746,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1792,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1816,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1845,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1869,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1881,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1891,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -1911,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -1931,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -1955,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -1987,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2016,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2039,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2051,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2084,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2104,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2165,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2179,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2234,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2251,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2273,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2297,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2316,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2333,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2375,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2385,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2396,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2421,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2431,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2445,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index e9d093861..12169112c 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -259,6 +259,10 @@ class NoirCircuitBuilder { this.setNoirConfigPreset(modNrPath, preset) } + if (modNrPath) { + this.setNoirConfigPreset(modNrPath, preset) + } + if (!this.options.noCleanTargets) { this.cleanTargetDirs(circuits) } From 916f084fb262359e22842aeb9c7a10ad489d4d45 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 10:24:42 +0200 Subject: [PATCH 12/54] add committee at construction and vec address --- .../src/threshold_plaintext_aggregator.rs | 40 +------------------ .../src/circuits/aggregation/node_dkg_fold.rs | 8 ++-- 2 files changed, 5 insertions(+), 43 deletions(-) diff --git a/crates/aggregator/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 98662f5bb..a2bd99f04 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -21,10 +21,7 @@ use e3_events::{ ZkResponse, }; use e3_fhe_params::BfvPreset; -use e3_sortition::{ - CommitteeMembersResponse, E3CommitteeContainsRequest, E3CommitteeContainsResponse, - GetCommitteeMembersRequest, Sortition, -}; +use e3_sortition::{E3CommitteeContainsRequest, E3CommitteeContainsResponse, Sortition}; use e3_trbfv::{ calculate_threshold_decryption::CalculateThresholdDecryptionRequest, TrBFVConfig, TrBFVRequest, TrBFVResponse, @@ -1077,41 +1074,6 @@ impl Handler>> } } -impl Handler for ThresholdPlaintextAggregator { - type Result = (); - - fn handle(&mut self, msg: CommitteeMembersResponse, ctx: &mut Self::Context) -> Self::Result { - let Some(members) = msg.members else { - warn!( - e3_id = %self.e3_id, - "committee members unavailable (E3 committee not finalized in sortition)" - ); - if let Some(ec) = self.last_ec.clone() { - let _ = self.fail_decryption_round(ec); - } - return; - }; - self.committee_members = Some(members); - if let Some(ec) = self.last_ec.clone() { - if let Err(e) = self.maybe_start_decryption_aggregation(&ec, ctx.address().recipient()) - { - warn!( - e3_id = %self.e3_id, - error = %e, - "maybe_start_decryption_aggregation failed after committee members response" - ); - } - if let Err(e) = self.try_publish_complete() { - warn!( - e3_id = %self.e3_id, - error = %e, - "try_publish_complete failed after committee members response" - ); - } - } - } -} - impl Handler> for ThresholdPlaintextAggregator { type Result = (); fn handle( diff --git a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs index 1c71ef037..8c4854f06 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -392,8 +392,8 @@ pub fn prove_dkg_aggregation( let committee_members: Vec = input .committee_addresses .iter() - .map(|addr| address_to_field_hex(addr)) - .collect::>()?; + .map(address_to_field_hex) + .collect(); let witness = DkgAggregatorWitness { nodes_fold_vk: nodes_fold_vk.verification_key.clone(), @@ -495,8 +495,8 @@ pub fn prove_decryption_aggregation_jobs( let committee_members: Vec = committee_addresses .iter() - .map(|addr| address_to_field_hex(addr)) - .collect::>()?; + .map(address_to_field_hex) + .collect(); let mut out = Vec::with_capacity(jobs.len()); for (i, job) in jobs.iter().enumerate() { From feea45dbb2977ae619534fac49e8f2df6fba839d Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 10:57:00 +0200 Subject: [PATCH 13/54] take care of nits --- .../results_insecure/crisp_verify_gas.json | 51 +- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 +++++------------ .../bfv/honk/DkgAggregatorVerifier.sol | 1635 +++++------------ 3 files changed, 973 insertions(+), 2348 deletions(-) diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 941915f4c..4fe6bf2c6 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -17,9 +17,9 @@ }, "calldata_gas": { "dkg": { - "proof": 169908, + "proof": 169968, "public_inputs": 6144, - "total": 176052 + "total": 176112 }, "dec": { "proof": 169848, @@ -121,6 +121,53 @@ } } }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.111521499, "runs": 3, "total_seconds": 0.334564499 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.610321888, "runs": 3, "total_seconds": 1.830965666 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.559050209, "runs": 1, "total_seconds": 0.559050209 }, + { "name": "GenEsiSss", "avg_seconds": 0.124032833, "runs": 3, "total_seconds": 0.372098501 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.226692527, "runs": 3, "total_seconds": 0.680077583 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.500284375, "runs": 1, "total_seconds": 8.500284375 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 49.366586083, "runs": 1, "total_seconds": 49.366586083 }, + { "name": "ZkDkgAggregation", "avg_seconds": 21.116986167, "runs": 1, "total_seconds": 21.116986167 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.465883083, "runs": 6, "total_seconds": 8.795298501 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 62.328322972, "runs": 3, "total_seconds": 186.984968916 }, + { "name": "ZkPkAggregation", "avg_seconds": 2.200691667, "runs": 1, "total_seconds": 2.200691667 }, + { "name": "ZkPkBfv", "avg_seconds": 0.336088264, "runs": 3, "total_seconds": 1.008264792 }, + { "name": "ZkPkGeneration", "avg_seconds": 1.351367042, "runs": 3, "total_seconds": 4.054101126 }, + { "name": "ZkShareComputation", "avg_seconds": 2.682164854, "runs": 6, "total_seconds": 16.092989126 }, + { "name": "ZkShareEncryption", "avg_seconds": 2.506225536, "runs": 24, "total_seconds": 60.149412873 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.176445291, "runs": 3, "total_seconds": 18.529335875 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.100550749, "runs": 3, "total_seconds": 0.301652249 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.221828033, "runs": 5, "total_seconds": 1.109140168 } + ], + "operation_timings_total_seconds": 381.986468376, + "timings_seconds": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9 }, + { "label": "Setup completed", "seconds": 3.0715085 }, + { "label": "Committee Setup Completed", "seconds": 20.22829075 }, + { "label": "Committee Finalization Complete", "seconds": 0.006707541 }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 304.143739958 }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 306.698077958 }, + { "label": "Application CT Gen", "seconds": 0.313315125 }, + { "label": "Running FHE Application", "seconds": 0.003688125 }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 79.884513625 }, + { "label": "Entire Test", "seconds": 410.2113715 } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + }, + "decryption_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } + }, "test_exit_code": { "crisp": 0, "folded_export": 0, diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index 110829355..d30ad9e01 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,238 +10,122 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), publicInputsSize: uint256(127), - ql: Honk.G1Point({ - x: uint256( - 0x21b909972bfe373b93f74e8d1b23d4f9da2d359033f9bea3bfd0dff4b14583b7 - ), - y: uint256( - 0x00cca3f61b1c83f29f138790d10246931d7ac90c67f0868928a8f87f115bae3f - ) + ql: Honk.G1Point({ + x: uint256(0x21b909972bfe373b93f74e8d1b23d4f9da2d359033f9bea3bfd0dff4b14583b7), + y: uint256(0x00cca3f61b1c83f29f138790d10246931d7ac90c67f0868928a8f87f115bae3f) }), - qr: Honk.G1Point({ - x: uint256( - 0x282acc4814ca5bff02f14c4a58b751ecc70c1c01494610385bff82d9072bf548 - ), - y: uint256( - 0x1d4bd882f21883ba61543964458224dbd4a53595d002e18c0db812390c45416c - ) + qr: Honk.G1Point({ + x: uint256(0x282acc4814ca5bff02f14c4a58b751ecc70c1c01494610385bff82d9072bf548), + y: uint256(0x1d4bd882f21883ba61543964458224dbd4a53595d002e18c0db812390c45416c) }), - qo: Honk.G1Point({ - x: uint256( - 0x1dd7761ff2c72c1af984fb53d1b3b42460c3323214a2d7dde42c62973d34198e - ), - y: uint256( - 0x13040272a4c0bedcfdec2b313f9efe3ee9a0bbd5ca58d9432dc8d832127e99b0 - ) + qo: Honk.G1Point({ + x: uint256(0x1dd7761ff2c72c1af984fb53d1b3b42460c3323214a2d7dde42c62973d34198e), + y: uint256(0x13040272a4c0bedcfdec2b313f9efe3ee9a0bbd5ca58d9432dc8d832127e99b0) }), - q4: Honk.G1Point({ - x: uint256( - 0x174f2955dfa54bf68658a287d4658af6994ecd888993452c4c14f1c60871ce56 - ), - y: uint256( - 0x0cda4ce133f1492a2f13a0e29c6df44b8e697f84e481f1b2dbf2d8d741be5e04 - ) + q4: Honk.G1Point({ + x: uint256(0x174f2955dfa54bf68658a287d4658af6994ecd888993452c4c14f1c60871ce56), + y: uint256(0x0cda4ce133f1492a2f13a0e29c6df44b8e697f84e481f1b2dbf2d8d741be5e04) }), - qm: Honk.G1Point({ - x: uint256( - 0x22ac07bc4c7d102054ba8dc18954f43d66ed6c57ede3a78e5fe44e80ab26daff - ), - y: uint256( - 0x01a5cce0a2e3607ae4fc406e7379aed53d7cd2cdb0d3a14e759531cee30cb9ea - ) + qm: Honk.G1Point({ + x: uint256(0x22ac07bc4c7d102054ba8dc18954f43d66ed6c57ede3a78e5fe44e80ab26daff), + y: uint256(0x01a5cce0a2e3607ae4fc406e7379aed53d7cd2cdb0d3a14e759531cee30cb9ea) }), - qc: Honk.G1Point({ - x: uint256( - 0x11fee8c098df12a40892852407a771a7d280dbfbab5eeb06b23896095ca7a290 - ), - y: uint256( - 0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b - ) + qc: Honk.G1Point({ + x: uint256(0x11fee8c098df12a40892852407a771a7d280dbfbab5eeb06b23896095ca7a290), + y: uint256(0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424 - ), - y: uint256( - 0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409 - ) + qLookup: Honk.G1Point({ + x: uint256(0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424), + y: uint256(0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409) }), - qArith: Honk.G1Point({ - x: uint256( - 0x2624d4d9d7eac2515cb4b322afc263ddc87c535791e2206eef64bc24024968e1 - ), - y: uint256( - 0x105a6a9c813245babf469aebeafca60e878d41b05f79125dedf362bee561b5ec - ) + qArith: Honk.G1Point({ + x: uint256(0x2624d4d9d7eac2515cb4b322afc263ddc87c535791e2206eef64bc24024968e1), + y: uint256(0x105a6a9c813245babf469aebeafca60e878d41b05f79125dedf362bee561b5ec) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x1cec49a84cd964f7dccf24f37f746eb4660ffa446ba4e79d04582d86fc5fb2be - ), - y: uint256( - 0x16acc276874333a56f75e2c79d9e723e9ac1bb18d1ab5bd579a3ab1702464ed0 - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x1cec49a84cd964f7dccf24f37f746eb4660ffa446ba4e79d04582d86fc5fb2be), + y: uint256(0x16acc276874333a56f75e2c79d9e723e9ac1bb18d1ab5bd579a3ab1702464ed0) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x006554df9837516dfb90ce208134e4b81d29ebf81032b08330501733f5f20d6a - ), - y: uint256( - 0x0ff31f52484554b3123ffc5c911d928e91ee373db03b305bd1350ae27aba4169 - ) + qElliptic: Honk.G1Point({ + x: uint256(0x006554df9837516dfb90ce208134e4b81d29ebf81032b08330501733f5f20d6a), + y: uint256(0x0ff31f52484554b3123ffc5c911d928e91ee373db03b305bd1350ae27aba4169) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x28fad415a8ba66a6c2d15321977f696a033b56580937a63c0be78be9ccdbf00a - ), - y: uint256( - 0x229fa12d35300e25b3095908acfed5751d51e93cd6ecf4af6757ba5a4c540c18 - ) + qMemory: Honk.G1Point({ + x: uint256(0x28fad415a8ba66a6c2d15321977f696a033b56580937a63c0be78be9ccdbf00a), + y: uint256(0x229fa12d35300e25b3095908acfed5751d51e93cd6ecf4af6757ba5a4c540c18) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x27769c90ab027f74a7f86fbe3a1832e41518cd4975e8ba110311664df43f0ce3 - ), - y: uint256( - 0x24a8f977133bcb034382e4cbcd3e335373ae5aac0e67824a2647554a52536b87 - ) + qNnf: Honk.G1Point({ + x: uint256(0x27769c90ab027f74a7f86fbe3a1832e41518cd4975e8ba110311664df43f0ce3), + y: uint256(0x24a8f977133bcb034382e4cbcd3e335373ae5aac0e67824a2647554a52536b87) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x2eb3443efed96b06718b28d1bfbbc35a407b6af60f720ba5a9d0ac78501f0ed2 - ), - y: uint256( - 0x17022aa4435561f83bdddcaa9174723a1e31c11d128a3455edc0b21bf22d334b - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x2eb3443efed96b06718b28d1bfbbc35a407b6af60f720ba5a9d0ac78501f0ed2), + y: uint256(0x17022aa4435561f83bdddcaa9174723a1e31c11d128a3455edc0b21bf22d334b) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x2ef9e66a814fe6821f53a2a2e1e93ac8630a347d7c9fee2afd2edcdc13bc0548 - ), - y: uint256( - 0x0ebdcee17969483e898170e905ff58418ad7e99173fa87c028966bd8c040c923 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x2ef9e66a814fe6821f53a2a2e1e93ac8630a347d7c9fee2afd2edcdc13bc0548), + y: uint256(0x0ebdcee17969483e898170e905ff58418ad7e99173fa87c028966bd8c040c923) }), - s1: Honk.G1Point({ - x: uint256( - 0x235a96328f656f5e8e3935de342e7ffb06d3400d2e11e03ff3f5e9729dec07de - ), - y: uint256( - 0x23b38403e02d9a93b48b7e21c10d7360fd7a39299f6aae2174d47e2df318775f - ) + s1: Honk.G1Point({ + x: uint256(0x235a96328f656f5e8e3935de342e7ffb06d3400d2e11e03ff3f5e9729dec07de), + y: uint256(0x23b38403e02d9a93b48b7e21c10d7360fd7a39299f6aae2174d47e2df318775f) }), - s2: Honk.G1Point({ - x: uint256( - 0x2c15698c01375d97f94676f1bbdb3ba5b157bacaadec11b12cf074cf212f6e12 - ), - y: uint256( - 0x141ec5c8ba7190c9cb1fab4f19e817d9d8f9cb2c0cedd614d4f7820a2f7c4abb - ) + s2: Honk.G1Point({ + x: uint256(0x2c15698c01375d97f94676f1bbdb3ba5b157bacaadec11b12cf074cf212f6e12), + y: uint256(0x141ec5c8ba7190c9cb1fab4f19e817d9d8f9cb2c0cedd614d4f7820a2f7c4abb) }), - s3: Honk.G1Point({ - x: uint256( - 0x22e7871e851cfc6514318d6f16d1c34305dd0e3c0dbe39df3527feda3b0d1eb0 - ), - y: uint256( - 0x078c546da57b7d1340a5a5b11922ab15592a2c3d32553532c318f0a238768a01 - ) + s3: Honk.G1Point({ + x: uint256(0x22e7871e851cfc6514318d6f16d1c34305dd0e3c0dbe39df3527feda3b0d1eb0), + y: uint256(0x078c546da57b7d1340a5a5b11922ab15592a2c3d32553532c318f0a238768a01) }), - s4: Honk.G1Point({ - x: uint256( - 0x17e47cca2b9876b87b90039176b89b889b2e6f6ab55bf5b6ade7026c1886a55b - ), - y: uint256( - 0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce - ) + s4: Honk.G1Point({ + x: uint256(0x17e47cca2b9876b87b90039176b89b889b2e6f6ab55bf5b6ade7026c1886a55b), + y: uint256(0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce) }), - t1: Honk.G1Point({ - x: uint256( - 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c - ), - y: uint256( - 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 - ) + t1: Honk.G1Point({ + x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), + y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) }), - t2: Honk.G1Point({ - x: uint256( - 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 - ), - y: uint256( - 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 - ) + t2: Honk.G1Point({ + x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), + y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) }), - t3: Honk.G1Point({ - x: uint256( - 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f - ), - y: uint256( - 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 - ) + t3: Honk.G1Point({ + x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), + y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) }), - t4: Honk.G1Point({ - x: uint256( - 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 - ), - y: uint256( - 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea - ) + t4: Honk.G1Point({ + x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), + y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) }), - id1: Honk.G1Point({ - x: uint256( - 0x162e6ffc2acbbe037aa8301684ed9e2d850a2c83a3c1a3164453b5c2187c8c75 - ), - y: uint256( - 0x22f0647302fbfc4d83670140b7ec0cd606fd991bd3e7cebeee96ee3b6169e538 - ) + id1: Honk.G1Point({ + x: uint256(0x162e6ffc2acbbe037aa8301684ed9e2d850a2c83a3c1a3164453b5c2187c8c75), + y: uint256(0x22f0647302fbfc4d83670140b7ec0cd606fd991bd3e7cebeee96ee3b6169e538) }), - id2: Honk.G1Point({ - x: uint256( - 0x1fc1d8dce21a638cd9695d5ed2d796b7b1423fe391391cbf0076dabcd5b1229c - ), - y: uint256( - 0x2e4d338298032c5426ca47e6970b8ef0b055728771a8ad6b455f4d3001abd402 - ) + id2: Honk.G1Point({ + x: uint256(0x1fc1d8dce21a638cd9695d5ed2d796b7b1423fe391391cbf0076dabcd5b1229c), + y: uint256(0x2e4d338298032c5426ca47e6970b8ef0b055728771a8ad6b455f4d3001abd402) }), - id3: Honk.G1Point({ - x: uint256( - 0x011c7ccc37d9abaf9dd6ffb88f045f8f6adb02dde453b8645b7a5461356255f8 - ), - y: uint256( - 0x0186d6fa335ec0a6179c9edeb2cfca478103eb4989218cd11ddeb6a4762ff294 - ) + id3: Honk.G1Point({ + x: uint256(0x011c7ccc37d9abaf9dd6ffb88f045f8f6adb02dde453b8645b7a5461356255f8), + y: uint256(0x0186d6fa335ec0a6179c9edeb2cfca478103eb4989218cd11ddeb6a4762ff294) }), - id4: Honk.G1Point({ - x: uint256( - 0x1be8e47ef6bff9941f3febe177d14f28448a16fe9dd81b1c9cfd05bd9136c02c - ), - y: uint256( - 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 - ) + id4: Honk.G1Point({ + x: uint256(0x1be8e47ef6bff9941f3febe177d14f28448a16fe9dd81b1c9cfd05bd9136c02c), + y: uint256(0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x201feccb28b5ddf7440c37e1a8d5676a8f9d7feb0e373436b3413fa9f775fd6b - ), - y: uint256( - 0x03f87d81d9e68bc20ce687e8a53620c9947d06fdc887f89e9fc6a023c8880e74 - ) + lagrangeLast: Honk.G1Point({ + x: uint256(0x201feccb28b5ddf7440c37e1a8d5676a8f9d7feb0e373436b3413fa9f775fd6b), + y: uint256(0x03f87d81d9e68bc20ce687e8a53620c9947d06fdc887f89e9fc6a023c8880e74) }) }); return vk; @@ -251,31 +135,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +298,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +496,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +530,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +542,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +551,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +567,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +588,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +606,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +617,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +657,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +684,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +701,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +765,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +783,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +842,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +861,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +881,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +892,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +911,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +956,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1096,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1104,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1118,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1128,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1203,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1228,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1264,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1278,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1300,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1309,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1353,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1402,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1463,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1494,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1515,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1536,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1572,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1594,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1628,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1651,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1698,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1714,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1726,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1746,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1792,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1816,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1845,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1869,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1881,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1891,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +1911,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +1931,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +1955,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +1987,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2016,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2039,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2051,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2084,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2104,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2165,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2179,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2234,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2251,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2273,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2297,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2316,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2333,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2375,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2385,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2396,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2421,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2431,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2445,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 8f8d5f63f..7dcc0571c 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,238 +10,122 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() - internal - pure - returns (Honk.VerificationKey memory) - { + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), publicInputsSize: uint256(31), - ql: Honk.G1Point({ - x: uint256( - 0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7 - ), - y: uint256( - 0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a - ) + ql: Honk.G1Point({ + x: uint256(0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7), + y: uint256(0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a) }), - qr: Honk.G1Point({ - x: uint256( - 0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77 - ), - y: uint256( - 0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94 - ) + qr: Honk.G1Point({ + x: uint256(0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77), + y: uint256(0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94) }), - qo: Honk.G1Point({ - x: uint256( - 0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507 - ), - y: uint256( - 0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda - ) + qo: Honk.G1Point({ + x: uint256(0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507), + y: uint256(0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda) }), - q4: Honk.G1Point({ - x: uint256( - 0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9 - ), - y: uint256( - 0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69 - ) + q4: Honk.G1Point({ + x: uint256(0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9), + y: uint256(0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69) }), - qm: Honk.G1Point({ - x: uint256( - 0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7 - ), - y: uint256( - 0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6 - ) + qm: Honk.G1Point({ + x: uint256(0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7), + y: uint256(0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6) }), - qc: Honk.G1Point({ - x: uint256( - 0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb - ), - y: uint256( - 0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095 - ) + qc: Honk.G1Point({ + x: uint256(0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb), + y: uint256(0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095) }), - qLookup: Honk.G1Point({ - x: uint256( - 0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea - ), - y: uint256( - 0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1 - ) + qLookup: Honk.G1Point({ + x: uint256(0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea), + y: uint256(0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1) }), - qArith: Honk.G1Point({ - x: uint256( - 0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a - ), - y: uint256( - 0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61 - ) + qArith: Honk.G1Point({ + x: uint256(0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a), + y: uint256(0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61) }), - qDeltaRange: Honk.G1Point({ - x: uint256( - 0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20 - ), - y: uint256( - 0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a - ) + qDeltaRange: Honk.G1Point({ + x: uint256(0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20), + y: uint256(0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a) }), - qElliptic: Honk.G1Point({ - x: uint256( - 0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d - ), - y: uint256( - 0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170 - ) + qElliptic: Honk.G1Point({ + x: uint256(0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d), + y: uint256(0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170) }), - qMemory: Honk.G1Point({ - x: uint256( - 0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b - ), - y: uint256( - 0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53 - ) + qMemory: Honk.G1Point({ + x: uint256(0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b), + y: uint256(0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53) }), - qNnf: Honk.G1Point({ - x: uint256( - 0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c - ), - y: uint256( - 0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0 - ) + qNnf: Honk.G1Point({ + x: uint256(0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c), + y: uint256(0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0) }), - qPoseidon2External: Honk.G1Point({ - x: uint256( - 0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7 - ), - y: uint256( - 0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce - ) + qPoseidon2External: Honk.G1Point({ + x: uint256(0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7), + y: uint256(0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256( - 0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351 - ), - y: uint256( - 0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77 - ) + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351), + y: uint256(0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77) }), - s1: Honk.G1Point({ - x: uint256( - 0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189 - ), - y: uint256( - 0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc - ) + s1: Honk.G1Point({ + x: uint256(0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189), + y: uint256(0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc) }), - s2: Honk.G1Point({ - x: uint256( - 0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393 - ), - y: uint256( - 0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c - ) + s2: Honk.G1Point({ + x: uint256(0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393), + y: uint256(0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c) }), - s3: Honk.G1Point({ - x: uint256( - 0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537 - ), - y: uint256( - 0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea - ) + s3: Honk.G1Point({ + x: uint256(0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537), + y: uint256(0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea) }), - s4: Honk.G1Point({ - x: uint256( - 0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab - ), - y: uint256( - 0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92 - ) + s4: Honk.G1Point({ + x: uint256(0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab), + y: uint256(0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92) }), - t1: Honk.G1Point({ - x: uint256( - 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c - ), - y: uint256( - 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 - ) + t1: Honk.G1Point({ + x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), + y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) }), - t2: Honk.G1Point({ - x: uint256( - 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 - ), - y: uint256( - 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 - ) + t2: Honk.G1Point({ + x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), + y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) }), - t3: Honk.G1Point({ - x: uint256( - 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f - ), - y: uint256( - 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 - ) + t3: Honk.G1Point({ + x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), + y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) }), - t4: Honk.G1Point({ - x: uint256( - 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 - ), - y: uint256( - 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea - ) + t4: Honk.G1Point({ + x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), + y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) }), - id1: Honk.G1Point({ - x: uint256( - 0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9 - ), - y: uint256( - 0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c - ) + id1: Honk.G1Point({ + x: uint256(0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9), + y: uint256(0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c) }), - id2: Honk.G1Point({ - x: uint256( - 0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266 - ), - y: uint256( - 0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883 - ) + id2: Honk.G1Point({ + x: uint256(0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266), + y: uint256(0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883) }), - id3: Honk.G1Point({ - x: uint256( - 0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d - ), - y: uint256( - 0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5 - ) + id3: Honk.G1Point({ + x: uint256(0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d), + y: uint256(0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5) }), - id4: Honk.G1Point({ - x: uint256( - 0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a - ), - y: uint256( - 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 - ) + id4: Honk.G1Point({ + x: uint256(0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a), + y: uint256(0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02) }), - lagrangeFirst: Honk.G1Point({ - x: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000001 - ), - y: uint256( - 0x0000000000000000000000000000000000000000000000000000000000000002 - ) + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) }), - lagrangeLast: Honk.G1Point({ - x: uint256( - 0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400 - ), - y: uint256( - 0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1 - ) + lagrangeLast: Honk.G1Point({ + x: uint256(0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400), + y: uint256(0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1) }) }); return vk; @@ -251,31 +135,24 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify( - bytes calldata _proof, - bytes32[] calldata _publicInputs - ) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap( - 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 -); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( - 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 -); +Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -421,11 +298,9 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + - NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -621,63 +496,26 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - ( - t.relationParameters, - previousChallenge - ) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); - (t.alphas, previousChallenge) = generateAlphaChallenges( - previousChallenge, - proof - ); + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - (t.gateChallenges, previousChallenge) = generateGateChallenges( - previousChallenge, - logN - ); - (t.libraChallenge, previousChallenge) = generateLibraChallenge( - previousChallenge, - proof - ); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( - proof, - previousChallenge, - logN - ); + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - (t.rho, previousChallenge) = generateRhoChallenge( - proof, - previousChallenge - ); + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - (t.geminiR, previousChallenge) = generateGeminiRChallenge( - proof, - previousChallenge, - logN - ); + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( - proof, - previousChallenge, - logN - ); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( - proof, - previousChallenge - ); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); return t; } - function splitChallenge( - Fr challenge - ) internal pure returns (Fr first, Fr second) { + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -692,23 +530,11 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) - internal - pure - returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) - { - ( - rp.eta, - rp.etaTwo, - rp.etaThree, - previousChallenge - ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - ( - rp.beta, - rp.gamma, - nextPreviousChallenge - ) = generateBetaAndGammaChallenges(previousChallenge, proof); + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -716,11 +542,7 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) - internal - pure - returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) - { + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -729,8 +551,7 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib - .toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -746,21 +567,18 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round0)) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (etaThree, ) = splitChallenge(previousChallenge); + (etaThree,) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) + { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -770,17 +588,12 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(round1)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -793,11 +606,9 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(alpha0)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); + (alpha,) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -806,54 +617,38 @@ library ZKTranscriptLib { } } - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) + function generateGateChallenges(Fr previousChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) - ); - (gateChallenges[0], ) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) + { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(challengeData)) - ); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) internal pure - returns ( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, - Fr nextPreviousChallenge - ) + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -862,27 +657,24 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(univariateChal)) - ); + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) + { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap( - proof.sumcheckEvaluations[i - 1] - ); + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -892,17 +684,15 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(rhoChallengeElements)) - ); - (rho, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) + { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -911,77 +701,59 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(gR)) - ); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - (geminiR, ) = splitChallenge(nextPreviousChallenge); + (geminiR,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[]( - logN + 1 + 4 - ); + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) + { + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.geminiAEvaluations[i - 1] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap( - proof.libraPolyEvals[libraIdx] - ); + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkNuChallengeElements)) - ); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) + { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32( - keccak256(abi.encodePacked(shplonkZChallengeElements)) - ); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); } - function loadProof( - bytes calldata proof, - uint256 logN - ) internal pure returns (Honk.ZKProof memory p) { + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -993,25 +765,17 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -1019,68 +783,48 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr( - proof[boundary:boundary + FIELD_ELEMENT_SIZE] - ); + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point( - proof[boundary:boundary + GROUP_ELEMENT_SIZE] - ); + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } } @@ -1098,60 +842,18 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePermutationRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateLogDerivativeLookupRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateDeltaRangeRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateEllipticRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulateMemoryRelation( - purportedEvaluations, - rp, - evaluations, - powPartialEval - ); - accumulateNnfRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonExternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); - accumulatePoseidonInternalRelation( - purportedEvaluations, - evaluations, - powPartialEval - ); + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations( - evaluations, - subrelationChallenges - ); + accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); } /** @@ -1159,15 +861,11 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -1183,16 +881,9 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -1201,10 +892,7 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + - wire(p, WIRE.W_4) - - wire(p, WIRE.W_L_SHIFT) + - wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -1223,67 +911,36 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + - wire(p, WIRE.ID_1) * - rp.beta + - rp.gamma; - num = - num * - (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = - num * - (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + - wire(p, WIRE.SIGMA_1) * - rp.beta + - rp.gamma; - den = - den * - (wire(p, WIRE.W_R) + - wire(p, WIRE.SIGMA_2) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_O) + - wire(p, WIRE.SIGMA_3) * - rp.beta + - rp.gamma); - den = - den * - (wire(p, WIRE.W_4) + - wire(p, WIRE.SIGMA_4) * - rp.beta + - rp.gamma); + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * - grand_product_numerator; - - acc = - acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + - (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * - grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * - wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -1299,52 +956,33 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + - rp.gamma + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + - wire(p, WIRE.Q_M) * - wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + - wire(p, WIRE.Q_C) * - wire(p, WIRE.W_O_SHIFT); - - read_term = - derived_entry_1 + - (derived_entry_2 * rp.eta) + - (derived_entry_3 * rp.etaTwo) + - (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = + wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * - write_term * - wire(p, WIRE.LOOKUP_INVERSES) - - inverse_exists_xor; + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * - read_inverse - - wire(p, WIRE.LOOKUP_READ_COUNTS) * - write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1458,11 +1096,7 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = - x_add_identity * - partialEval * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1470,15 +1104,8 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * - x_diff + - (ep.x_3 - ep.x_1) * - y_diff; - evals[12] = - y_add_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1491,15 +1118,9 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = - (ep.x_3 + ep.x_1 + ep.x_1) * - y1_sqr_mul_4 - - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; evals[11] = evals[11] + acc; } @@ -1507,16 +1128,8 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * - (ep.x_1 - ep.x_3) - - (ep.y_1 + ep.y_1) * - (ep.y_1 + ep.y_3); - evals[12] = - evals[12] + - y_double_identity * - domainSep * - wire(p, WIRE.Q_ELLIPTIC) * - q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; } } @@ -1590,12 +1203,8 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = - ap.memory_record_check + - (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1619,26 +1228,16 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = - ap.index_delta * - (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = - (ap.index_delta * MINUS_ONE + ONE) * - ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * 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 @@ -1665,22 +1264,13 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = - ap.next_gate_access_type + - (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = - wire(p, WIRE.W_4_SHIFT) - - ap.next_gate_access_type; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1688,28 +1278,15 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * - ap.next_gate_access_type - - ap.next_gate_access_type; + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; // Putting it all together... - evals[16] = - ap - .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = - ap.index_is_monotonically_increasing * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = - ap.next_gate_access_type_is_boolean * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = - ap.access_check * - (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1723,10 +1300,7 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = - (ap.index_delta * MINUS_ONE + ONE) * - ap.timestamp_delta - - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1735,21 +1309,12 @@ 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.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = - ap.memory_identity * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1788,56 +1353,28 @@ library RelationsLib { * * */ - ap.limb_subproduct = - wire(p, WIRE.W_L) * - wire(p, WIRE.W_R_SHIFT) + - wire(p, WIRE.W_L_SHIFT) * - wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * - wire(p, WIRE.W_4) + - wire(p, WIRE.W_R) * - wire(p, WIRE.W_O) - - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 - - wire(p, WIRE.W_4_SHIFT); + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 + - ap.limb_subproduct; - ap.non_native_field_gate_2 = - ap.non_native_field_gate_2 * - wire(p, WIRE.Q_4); + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = - ap.limb_subproduct + - (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 - - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = - ap.non_native_field_gate_1 * - wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 + - wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 - - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = - ap.non_native_field_gate_3 * - wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + - ap.non_native_field_gate_2 + - ap.non_native_field_gate_3; - non_native_field_identity = - non_native_field_identity * - wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1865,11 +1402,8 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + - ap.limb_accumulator_2; - limb_accumulator_identity = - limb_accumulator_identity * - wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1929,25 +1463,13 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = - evals[20] + - ep.q_pos_by_scaling * - (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = - evals[21] + - ep.q_pos_by_scaling * - (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = - evals[22] + - ep.q_pos_by_scaling * - (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = - evals[23] + - ep.q_pos_by_scaling * - (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1972,18 +1494,10 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from( - 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 - ), - FrLib.from( - 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b - ), - FrLib.from( - 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 - ), - FrLib.from( - 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b - ) + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) ]; // add round constants @@ -2001,28 +1515,16 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = - evals[24] + - ip.q_pos_by_scaling * - (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = - evals[25] + - ip.q_pos_by_scaling * - (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = - evals[26] + - ip.q_pos_by_scaling * - (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = - evals[27] + - ip.q_pos_by_scaling * - (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -2034,10 +1536,7 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = - accumulator + - evaluations[i] * - subrelationChallenges[i - 1]; + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; } } } @@ -2073,10 +1572,7 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares( - Fr r, - uint256 logN - ) internal pure returns (Fr[] memory) { + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -2098,15 +1594,10 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * - batchedEvalAccumulator * - Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] + * (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = - batchedEvalRoundAcc * - (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -2137,18 +1628,13 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point( - bytes calldata proofSection -) pure returns (Honk.G1Point memory point) { +function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, - y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace( - Honk.G1Point memory point -) pure returns (Honk.G1Point memory) { +function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -2165,9 +1651,10 @@ function negateInplace( * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -2211,10 +1698,7 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - ( - Honk.G1Point memory proofLhs, - Honk.G1Point memory proofRhs - ) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -2230,9 +1714,7 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32( - keccak256(abi.encodePacked(recursionSeparatorElements)) - ); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -2244,11 +1726,10 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -2265,10 +1746,7 @@ function mulWithSeperator( * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul( - Fr value, - Honk.G1Point memory point -) view returns (Honk.G1Point memory) { +function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2314,10 +1792,7 @@ function ecMul( * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd( - Honk.G1Point memory lhs, - Honk.G1Point memory rhs -) view returns (Honk.G1Point memory) { +function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -2341,9 +1816,7 @@ function ecAdd( // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } + if iszero(success) { revert(0, 0) } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -2372,41 +1845,22 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing( - Honk.G1Point memory rhs, - Honk.G1Point memory lhs -) view returns (bool decodedResult) { +function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256( - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - ), - uint256( - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ), - uint256( - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - ), - uint256( - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ), + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), lhs.x, lhs.y, // G2 point from VK - uint256( - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 - ), - uint256( - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ), - uint256( - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 - ), - uint256( - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ) + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -2415,6 +1869,9 @@ function pairing( // Field arithmetic libraries - prevent littering the code with modmul / addmul + + + abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -2424,12 +1881,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor( - uint256 _N, - uint256 _logN, - uint256 _vkHash, - uint256 _numPublicInputs - ) { + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -2439,11 +1891,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN( - uint256 logN, - uint256 actualLength, - uint256 expectedLength - ); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -2463,10 +1911,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += - logN * - ZK_BATCHED_RELATION_PARTIAL_LENGTH * - NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -2486,26 +1931,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() - internal - pure - virtual - returns (Honk.VerificationKey memory); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); - function verify( - bytes calldata proof, - bytes32[] calldata publicInputs - ) public view override returns (bool verified) { + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) + { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN( - $LOG_N, - proof.length, - expectedProofSize * 32 - ); + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -2516,20 +1955,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript( - p, - publicInputs, - $VK_HASH, - $NUM_PUBLIC_INPUTS, - $LOG_N - ); + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, + t.relationParameters.gamma, /*pubInputsOffset=*/ 1 ); @@ -2553,16 +1987,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + - (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for ( - uint256 i = 0; - i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; - i++ - ) { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2587,32 +2016,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck( - Honk.ZKProof memory proof, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum( - roundUnivariate, - roundChallenge - ); + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); powPartialEvaluation = - powPartialEvaluation * - (Fr.wrap(1) + - roundChallenge * - (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2620,15 +2039,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[ - i + NUM_MASKING_POLYNOMIALS - ]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation + relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2637,48 +2051,27 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * - (Fr.wrap(1) - evaluation) + - proof.libraEvaluation * - tp.libraChallenge; + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] - memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000000240 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 - ), - Fr.wrap( - 0x00000000000000000000000000000000000000000000000000000000000005a0 - ), - Fr.wrap( - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 - ), - Fr.wrap( - 0x0000000000000000000000000000000000000000000000000000000000009d80 - ) - ]; + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2691,17 +2084,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert( - BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * - (roundChallenge - Fr.wrap(i)) - ); + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = - targetSum + - roundUnivariates[i] * - denominatorInverses[i]; + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2717,63 +2104,56 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib - .computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = - mem.posInvertedDenominator + - (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * - (mem.posInvertedDenominator - - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2785,10 +2165,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * - mem.batchingChallenge); + mem.batchedEvaluation = mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2801,13 +2179,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = - scalars[scalarOff] + - (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + - (proof.sumcheckEvaluations[evaluationOff] * - mem.batchingChallenge); + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2860,15 +2234,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2877,23 +2251,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib - .computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - foldPosEvaluations[0] * - mem.posInvertedDenominator; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - (proof.geminiAEvaluations[0] * - tp.shplonkNu * - mem.negInvertedDenominator); + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2905,40 +2273,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + - powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = - mem.batchingChallenge * - mem.posInvertedDenominator; - mem.scalingFactorNeg = - mem.batchingChallenge * - tp.shplonkNu * - mem.negInvertedDenominator; - scalars[boundary + i] = - mem.scalingFactorNeg.neg() + - mem.scalingFactorPos.neg(); + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * - proof.geminiAEvaluations[i + 1]; - accumContribution = - accumContribution + - mem.scalingFactorPos * - foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - accumContribution; + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; } // Update the running power of v - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2947,24 +2297,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div( - tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR - ); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = - mem.batchingChallenge * - tp.shplonkNu * - tp.shplonkNu; + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = - mem.constantTermAccumulator + - scalingFactor * - proof.libraPolyEvals[i]; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2974,17 +2316,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); scalars[boundary++] = mem.constantTermAccumulator; - if ( - !checkEvalsConsistency( - proof.libraPolyEvals, - tp.geminiR, - tp.sumCheckUChallenges, - proof.libraEvaluation - ) - ) { + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { revert ConsistencyCheckFailed(); } @@ -2998,15 +2333,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator( - proof.pairingPointObject, - pair.P_0, - pair.P_1 - ); - ( - Honk.G1Point memory P_0_other, - Honk.G1Point memory P_1_other - ) = convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -3046,14 +2375,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for ( - uint256 idx = currIdx + 1; - idx < currIdx + LIBRA_UNIVARIATES_LENGTH; - idx++ - ) { - mem.challengePolyLagrange[idx] = - mem.challengePolyLagrange[idx - 1] * - uChallenges[round]; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; } } @@ -3062,10 +2385,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = - mem.challengePolyEval + - mem.challengePolyLagrange[idx] * - mem.denominators[idx]; + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -3076,28 +2396,19 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - - libraPolyEvals[2] - - libraPolyEvals[0] * - mem.challengePolyEval); - mem.diff = - mem.diff + - mem.lagrangeLast * - (libraPolyEvals[2] - libraEval) - - vanishingPolyEval * - libraPolyEvals[3]; + mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul( - Honk.G1Point[] memory base, - Fr[] memory scalars - ) internal view returns (Honk.G1Point memory result) { + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -3110,9 +2421,7 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -3122,22 +2431,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and( - success, - staticcall( - gas(), - 7, - add(free, 0x40), - 0x60, - add(free, 0x40), - 0x40 - ) - ) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) // accumulator = accumulator + accumulator_2 - success := and( - success, - staticcall(gas(), 6, free, 0x80, free, 0x40) - ) + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) } // Return the result @@ -3149,15 +2445,8 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is - BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) -{ - function loadVerificationKey() - internal - pure - override - returns (Honk.VerificationKey memory) - { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); } } From 9dc4453bf7796f6834198a2dcdcab904401eddaa Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 10:57:33 +0200 Subject: [PATCH 14/54] fmt --- .../bfv/honk/DecryptionAggregatorVerifier.sol | 1635 ++++++++++++----- .../bfv/honk/DkgAggregatorVerifier.sol | 1635 ++++++++++++----- 2 files changed, 2346 insertions(+), 924 deletions(-) diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol index d30ad9e01..110829355 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol @@ -10,122 +10,238 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 127; uint256 constant VK_HASH = 0x179aeedaf3c48066180561e127d73c1ffbabf175e47589b309ddec6b1cd679d3; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), publicInputsSize: uint256(127), - ql: Honk.G1Point({ - x: uint256(0x21b909972bfe373b93f74e8d1b23d4f9da2d359033f9bea3bfd0dff4b14583b7), - y: uint256(0x00cca3f61b1c83f29f138790d10246931d7ac90c67f0868928a8f87f115bae3f) + ql: Honk.G1Point({ + x: uint256( + 0x21b909972bfe373b93f74e8d1b23d4f9da2d359033f9bea3bfd0dff4b14583b7 + ), + y: uint256( + 0x00cca3f61b1c83f29f138790d10246931d7ac90c67f0868928a8f87f115bae3f + ) }), - qr: Honk.G1Point({ - x: uint256(0x282acc4814ca5bff02f14c4a58b751ecc70c1c01494610385bff82d9072bf548), - y: uint256(0x1d4bd882f21883ba61543964458224dbd4a53595d002e18c0db812390c45416c) + qr: Honk.G1Point({ + x: uint256( + 0x282acc4814ca5bff02f14c4a58b751ecc70c1c01494610385bff82d9072bf548 + ), + y: uint256( + 0x1d4bd882f21883ba61543964458224dbd4a53595d002e18c0db812390c45416c + ) }), - qo: Honk.G1Point({ - x: uint256(0x1dd7761ff2c72c1af984fb53d1b3b42460c3323214a2d7dde42c62973d34198e), - y: uint256(0x13040272a4c0bedcfdec2b313f9efe3ee9a0bbd5ca58d9432dc8d832127e99b0) + qo: Honk.G1Point({ + x: uint256( + 0x1dd7761ff2c72c1af984fb53d1b3b42460c3323214a2d7dde42c62973d34198e + ), + y: uint256( + 0x13040272a4c0bedcfdec2b313f9efe3ee9a0bbd5ca58d9432dc8d832127e99b0 + ) }), - q4: Honk.G1Point({ - x: uint256(0x174f2955dfa54bf68658a287d4658af6994ecd888993452c4c14f1c60871ce56), - y: uint256(0x0cda4ce133f1492a2f13a0e29c6df44b8e697f84e481f1b2dbf2d8d741be5e04) + q4: Honk.G1Point({ + x: uint256( + 0x174f2955dfa54bf68658a287d4658af6994ecd888993452c4c14f1c60871ce56 + ), + y: uint256( + 0x0cda4ce133f1492a2f13a0e29c6df44b8e697f84e481f1b2dbf2d8d741be5e04 + ) }), - qm: Honk.G1Point({ - x: uint256(0x22ac07bc4c7d102054ba8dc18954f43d66ed6c57ede3a78e5fe44e80ab26daff), - y: uint256(0x01a5cce0a2e3607ae4fc406e7379aed53d7cd2cdb0d3a14e759531cee30cb9ea) + qm: Honk.G1Point({ + x: uint256( + 0x22ac07bc4c7d102054ba8dc18954f43d66ed6c57ede3a78e5fe44e80ab26daff + ), + y: uint256( + 0x01a5cce0a2e3607ae4fc406e7379aed53d7cd2cdb0d3a14e759531cee30cb9ea + ) }), - qc: Honk.G1Point({ - x: uint256(0x11fee8c098df12a40892852407a771a7d280dbfbab5eeb06b23896095ca7a290), - y: uint256(0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b) + qc: Honk.G1Point({ + x: uint256( + 0x11fee8c098df12a40892852407a771a7d280dbfbab5eeb06b23896095ca7a290 + ), + y: uint256( + 0x17ec33cd33eacb4335ba2e0b3baffe2b0bd0f8371c7cf7213447d3ba6dd4ba6b + ) }), - qLookup: Honk.G1Point({ - x: uint256(0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424), - y: uint256(0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409) + qLookup: Honk.G1Point({ + x: uint256( + 0x13143d24a192079453fc93ca72b6be61609f4d042621b3d3973bfa341bb8a424 + ), + y: uint256( + 0x2dee6429bc80fc94550da46393279fc6e08def2542d28bbc312a082f31e56409 + ) }), - qArith: Honk.G1Point({ - x: uint256(0x2624d4d9d7eac2515cb4b322afc263ddc87c535791e2206eef64bc24024968e1), - y: uint256(0x105a6a9c813245babf469aebeafca60e878d41b05f79125dedf362bee561b5ec) + qArith: Honk.G1Point({ + x: uint256( + 0x2624d4d9d7eac2515cb4b322afc263ddc87c535791e2206eef64bc24024968e1 + ), + y: uint256( + 0x105a6a9c813245babf469aebeafca60e878d41b05f79125dedf362bee561b5ec + ) }), - qDeltaRange: Honk.G1Point({ - x: uint256(0x1cec49a84cd964f7dccf24f37f746eb4660ffa446ba4e79d04582d86fc5fb2be), - y: uint256(0x16acc276874333a56f75e2c79d9e723e9ac1bb18d1ab5bd579a3ab1702464ed0) + qDeltaRange: Honk.G1Point({ + x: uint256( + 0x1cec49a84cd964f7dccf24f37f746eb4660ffa446ba4e79d04582d86fc5fb2be + ), + y: uint256( + 0x16acc276874333a56f75e2c79d9e723e9ac1bb18d1ab5bd579a3ab1702464ed0 + ) }), - qElliptic: Honk.G1Point({ - x: uint256(0x006554df9837516dfb90ce208134e4b81d29ebf81032b08330501733f5f20d6a), - y: uint256(0x0ff31f52484554b3123ffc5c911d928e91ee373db03b305bd1350ae27aba4169) + qElliptic: Honk.G1Point({ + x: uint256( + 0x006554df9837516dfb90ce208134e4b81d29ebf81032b08330501733f5f20d6a + ), + y: uint256( + 0x0ff31f52484554b3123ffc5c911d928e91ee373db03b305bd1350ae27aba4169 + ) }), - qMemory: Honk.G1Point({ - x: uint256(0x28fad415a8ba66a6c2d15321977f696a033b56580937a63c0be78be9ccdbf00a), - y: uint256(0x229fa12d35300e25b3095908acfed5751d51e93cd6ecf4af6757ba5a4c540c18) + qMemory: Honk.G1Point({ + x: uint256( + 0x28fad415a8ba66a6c2d15321977f696a033b56580937a63c0be78be9ccdbf00a + ), + y: uint256( + 0x229fa12d35300e25b3095908acfed5751d51e93cd6ecf4af6757ba5a4c540c18 + ) }), - qNnf: Honk.G1Point({ - x: uint256(0x27769c90ab027f74a7f86fbe3a1832e41518cd4975e8ba110311664df43f0ce3), - y: uint256(0x24a8f977133bcb034382e4cbcd3e335373ae5aac0e67824a2647554a52536b87) + qNnf: Honk.G1Point({ + x: uint256( + 0x27769c90ab027f74a7f86fbe3a1832e41518cd4975e8ba110311664df43f0ce3 + ), + y: uint256( + 0x24a8f977133bcb034382e4cbcd3e335373ae5aac0e67824a2647554a52536b87 + ) }), - qPoseidon2External: Honk.G1Point({ - x: uint256(0x2eb3443efed96b06718b28d1bfbbc35a407b6af60f720ba5a9d0ac78501f0ed2), - y: uint256(0x17022aa4435561f83bdddcaa9174723a1e31c11d128a3455edc0b21bf22d334b) + qPoseidon2External: Honk.G1Point({ + x: uint256( + 0x2eb3443efed96b06718b28d1bfbbc35a407b6af60f720ba5a9d0ac78501f0ed2 + ), + y: uint256( + 0x17022aa4435561f83bdddcaa9174723a1e31c11d128a3455edc0b21bf22d334b + ) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x2ef9e66a814fe6821f53a2a2e1e93ac8630a347d7c9fee2afd2edcdc13bc0548), - y: uint256(0x0ebdcee17969483e898170e905ff58418ad7e99173fa87c028966bd8c040c923) + qPoseidon2Internal: Honk.G1Point({ + x: uint256( + 0x2ef9e66a814fe6821f53a2a2e1e93ac8630a347d7c9fee2afd2edcdc13bc0548 + ), + y: uint256( + 0x0ebdcee17969483e898170e905ff58418ad7e99173fa87c028966bd8c040c923 + ) }), - s1: Honk.G1Point({ - x: uint256(0x235a96328f656f5e8e3935de342e7ffb06d3400d2e11e03ff3f5e9729dec07de), - y: uint256(0x23b38403e02d9a93b48b7e21c10d7360fd7a39299f6aae2174d47e2df318775f) + s1: Honk.G1Point({ + x: uint256( + 0x235a96328f656f5e8e3935de342e7ffb06d3400d2e11e03ff3f5e9729dec07de + ), + y: uint256( + 0x23b38403e02d9a93b48b7e21c10d7360fd7a39299f6aae2174d47e2df318775f + ) }), - s2: Honk.G1Point({ - x: uint256(0x2c15698c01375d97f94676f1bbdb3ba5b157bacaadec11b12cf074cf212f6e12), - y: uint256(0x141ec5c8ba7190c9cb1fab4f19e817d9d8f9cb2c0cedd614d4f7820a2f7c4abb) + s2: Honk.G1Point({ + x: uint256( + 0x2c15698c01375d97f94676f1bbdb3ba5b157bacaadec11b12cf074cf212f6e12 + ), + y: uint256( + 0x141ec5c8ba7190c9cb1fab4f19e817d9d8f9cb2c0cedd614d4f7820a2f7c4abb + ) }), - s3: Honk.G1Point({ - x: uint256(0x22e7871e851cfc6514318d6f16d1c34305dd0e3c0dbe39df3527feda3b0d1eb0), - y: uint256(0x078c546da57b7d1340a5a5b11922ab15592a2c3d32553532c318f0a238768a01) + s3: Honk.G1Point({ + x: uint256( + 0x22e7871e851cfc6514318d6f16d1c34305dd0e3c0dbe39df3527feda3b0d1eb0 + ), + y: uint256( + 0x078c546da57b7d1340a5a5b11922ab15592a2c3d32553532c318f0a238768a01 + ) }), - s4: Honk.G1Point({ - x: uint256(0x17e47cca2b9876b87b90039176b89b889b2e6f6ab55bf5b6ade7026c1886a55b), - y: uint256(0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce) + s4: Honk.G1Point({ + x: uint256( + 0x17e47cca2b9876b87b90039176b89b889b2e6f6ab55bf5b6ade7026c1886a55b + ), + y: uint256( + 0x1277395e4b6af40bd3099eedef9f6f1f4a3f6e95a1c0540bc521df5df391fdce + ) }), - t1: Honk.G1Point({ - x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), - y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) + t1: Honk.G1Point({ + x: uint256( + 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c + ), + y: uint256( + 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 + ) }), - t2: Honk.G1Point({ - x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), - y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) + t2: Honk.G1Point({ + x: uint256( + 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 + ), + y: uint256( + 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 + ) }), - t3: Honk.G1Point({ - x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), - y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) + t3: Honk.G1Point({ + x: uint256( + 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f + ), + y: uint256( + 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 + ) }), - t4: Honk.G1Point({ - x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), - y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) + t4: Honk.G1Point({ + x: uint256( + 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 + ), + y: uint256( + 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea + ) }), - id1: Honk.G1Point({ - x: uint256(0x162e6ffc2acbbe037aa8301684ed9e2d850a2c83a3c1a3164453b5c2187c8c75), - y: uint256(0x22f0647302fbfc4d83670140b7ec0cd606fd991bd3e7cebeee96ee3b6169e538) + id1: Honk.G1Point({ + x: uint256( + 0x162e6ffc2acbbe037aa8301684ed9e2d850a2c83a3c1a3164453b5c2187c8c75 + ), + y: uint256( + 0x22f0647302fbfc4d83670140b7ec0cd606fd991bd3e7cebeee96ee3b6169e538 + ) }), - id2: Honk.G1Point({ - x: uint256(0x1fc1d8dce21a638cd9695d5ed2d796b7b1423fe391391cbf0076dabcd5b1229c), - y: uint256(0x2e4d338298032c5426ca47e6970b8ef0b055728771a8ad6b455f4d3001abd402) + id2: Honk.G1Point({ + x: uint256( + 0x1fc1d8dce21a638cd9695d5ed2d796b7b1423fe391391cbf0076dabcd5b1229c + ), + y: uint256( + 0x2e4d338298032c5426ca47e6970b8ef0b055728771a8ad6b455f4d3001abd402 + ) }), - id3: Honk.G1Point({ - x: uint256(0x011c7ccc37d9abaf9dd6ffb88f045f8f6adb02dde453b8645b7a5461356255f8), - y: uint256(0x0186d6fa335ec0a6179c9edeb2cfca478103eb4989218cd11ddeb6a4762ff294) + id3: Honk.G1Point({ + x: uint256( + 0x011c7ccc37d9abaf9dd6ffb88f045f8f6adb02dde453b8645b7a5461356255f8 + ), + y: uint256( + 0x0186d6fa335ec0a6179c9edeb2cfca478103eb4989218cd11ddeb6a4762ff294 + ) }), - id4: Honk.G1Point({ - x: uint256(0x1be8e47ef6bff9941f3febe177d14f28448a16fe9dd81b1c9cfd05bd9136c02c), - y: uint256(0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0) + id4: Honk.G1Point({ + x: uint256( + 0x1be8e47ef6bff9941f3febe177d14f28448a16fe9dd81b1c9cfd05bd9136c02c + ), + y: uint256( + 0x1d6ea8c9b1f0fd0d27694dee140ef177141fc8e1d240e5715834070a82a9d7e0 + ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), - lagrangeLast: Honk.G1Point({ - x: uint256(0x201feccb28b5ddf7440c37e1a8d5676a8f9d7feb0e373436b3413fa9f775fd6b), - y: uint256(0x03f87d81d9e68bc20ce687e8a53620c9947d06fdc887f89e9fc6a023c8880e74) + lagrangeLast: Honk.G1Point({ + x: uint256( + 0x201feccb28b5ddf7440c37e1a8d5676a8f9d7feb0e373436b3413fa9f775fd6b + ), + y: uint256( + 0x03f87d81d9e68bc20ce687e8a53620c9947d06fdc887f89e9fc6a023c8880e74 + ) }) }); return vk; @@ -135,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -298,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -496,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -530,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -542,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -551,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -567,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -588,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -606,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -617,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -657,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -684,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -701,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -765,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -783,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -842,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -861,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -881,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -892,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -911,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -956,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1096,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1104,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1118,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1128,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1203,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1228,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1264,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1278,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1300,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1309,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1353,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1402,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1463,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1494,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1515,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1536,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1572,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1594,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1628,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1651,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1698,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1714,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1726,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1746,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1792,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1816,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1845,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1869,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1881,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1891,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -1911,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -1931,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -1955,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -1987,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2016,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2039,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2051,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2084,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2104,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2165,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2179,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2234,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2251,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2273,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2297,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2316,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2333,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2375,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2385,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2396,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2421,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2431,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2445,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DecryptionAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DecryptionAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 7dcc0571c..8f8d5f63f 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -10,122 +10,238 @@ uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + function loadVerificationKey() + internal + pure + returns (Honk.VerificationKey memory) + { Honk.VerificationKey memory vk = Honk.VerificationKey({ circuitSize: uint256(2097152), logCircuitSize: uint256(21), publicInputsSize: uint256(31), - ql: Honk.G1Point({ - x: uint256(0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7), - y: uint256(0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a) + ql: Honk.G1Point({ + x: uint256( + 0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7 + ), + y: uint256( + 0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a + ) }), - qr: Honk.G1Point({ - x: uint256(0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77), - y: uint256(0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94) + qr: Honk.G1Point({ + x: uint256( + 0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77 + ), + y: uint256( + 0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94 + ) }), - qo: Honk.G1Point({ - x: uint256(0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507), - y: uint256(0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda) + qo: Honk.G1Point({ + x: uint256( + 0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507 + ), + y: uint256( + 0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda + ) }), - q4: Honk.G1Point({ - x: uint256(0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9), - y: uint256(0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69) + q4: Honk.G1Point({ + x: uint256( + 0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9 + ), + y: uint256( + 0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69 + ) }), - qm: Honk.G1Point({ - x: uint256(0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7), - y: uint256(0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6) + qm: Honk.G1Point({ + x: uint256( + 0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7 + ), + y: uint256( + 0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6 + ) }), - qc: Honk.G1Point({ - x: uint256(0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb), - y: uint256(0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095) + qc: Honk.G1Point({ + x: uint256( + 0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb + ), + y: uint256( + 0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095 + ) }), - qLookup: Honk.G1Point({ - x: uint256(0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea), - y: uint256(0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1) + qLookup: Honk.G1Point({ + x: uint256( + 0x182070aa2b03de9e8b4cada6a0760a0c6c72852783e7db97b4cd91281c03d3ea + ), + y: uint256( + 0x08c55ecc2b52f5393505462ae16474727d81f865b293adee900644436146a8e1 + ) }), - qArith: Honk.G1Point({ - x: uint256(0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a), - y: uint256(0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61) + qArith: Honk.G1Point({ + x: uint256( + 0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a + ), + y: uint256( + 0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61 + ) }), - qDeltaRange: Honk.G1Point({ - x: uint256(0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20), - y: uint256(0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a) + qDeltaRange: Honk.G1Point({ + x: uint256( + 0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20 + ), + y: uint256( + 0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a + ) }), - qElliptic: Honk.G1Point({ - x: uint256(0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d), - y: uint256(0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170) + qElliptic: Honk.G1Point({ + x: uint256( + 0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d + ), + y: uint256( + 0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170 + ) }), - qMemory: Honk.G1Point({ - x: uint256(0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b), - y: uint256(0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53) + qMemory: Honk.G1Point({ + x: uint256( + 0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b + ), + y: uint256( + 0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53 + ) }), - qNnf: Honk.G1Point({ - x: uint256(0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c), - y: uint256(0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0) + qNnf: Honk.G1Point({ + x: uint256( + 0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c + ), + y: uint256( + 0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0 + ) }), - qPoseidon2External: Honk.G1Point({ - x: uint256(0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7), - y: uint256(0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce) + qPoseidon2External: Honk.G1Point({ + x: uint256( + 0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7 + ), + y: uint256( + 0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce + ) }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351), - y: uint256(0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77) + qPoseidon2Internal: Honk.G1Point({ + x: uint256( + 0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351 + ), + y: uint256( + 0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77 + ) }), - s1: Honk.G1Point({ - x: uint256(0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189), - y: uint256(0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc) + s1: Honk.G1Point({ + x: uint256( + 0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189 + ), + y: uint256( + 0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc + ) }), - s2: Honk.G1Point({ - x: uint256(0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393), - y: uint256(0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c) + s2: Honk.G1Point({ + x: uint256( + 0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393 + ), + y: uint256( + 0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c + ) }), - s3: Honk.G1Point({ - x: uint256(0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537), - y: uint256(0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea) + s3: Honk.G1Point({ + x: uint256( + 0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537 + ), + y: uint256( + 0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea + ) }), - s4: Honk.G1Point({ - x: uint256(0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab), - y: uint256(0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92) + s4: Honk.G1Point({ + x: uint256( + 0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab + ), + y: uint256( + 0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92 + ) }), - t1: Honk.G1Point({ - x: uint256(0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c), - y: uint256(0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579) + t1: Honk.G1Point({ + x: uint256( + 0x0bed9c3687f3524dbbb6410842f20eb0d87d1915348d97dd74ce9df8681fb03c + ), + y: uint256( + 0x061cf87194c9b570a8d060c9dfed139083f2aedc80da0d97d390d72f5cc75579 + ) }), - t2: Honk.G1Point({ - x: uint256(0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887), - y: uint256(0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8) + t2: Honk.G1Point({ + x: uint256( + 0x0441aaeda5bb8ccbef2c72be215aacd45db72650f5a9855820447b241f57c887 + ), + y: uint256( + 0x27e4f80d4673c2dc9bc21386edf443e8f74d4cff7b89fb2c34c0bcca5008d9d8 + ) }), - t3: Honk.G1Point({ - x: uint256(0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f), - y: uint256(0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7) + t3: Honk.G1Point({ + x: uint256( + 0x17161957b5bea1c4b6cd7dd7a0b530aae4907cffac5801fff85ba8e4c3fa3f2f + ), + y: uint256( + 0x14669badaf49b0e6aaa983ac2a20378e7c5ac3b4141284ca01124c3dd33589f7 + ) }), - t4: Honk.G1Point({ - x: uint256(0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6), - y: uint256(0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea) + t4: Honk.G1Point({ + x: uint256( + 0x1e10d6c8482b99a03f78a2028bb33719c19bc62fa08e1d548059b139388532e6 + ), + y: uint256( + 0x0139852d1968d8a0c11ba44db1553094224570b77f987b01a73781e265365cea + ) }), - id1: Honk.G1Point({ - x: uint256(0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9), - y: uint256(0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c) + id1: Honk.G1Point({ + x: uint256( + 0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9 + ), + y: uint256( + 0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c + ) }), - id2: Honk.G1Point({ - x: uint256(0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266), - y: uint256(0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883) + id2: Honk.G1Point({ + x: uint256( + 0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266 + ), + y: uint256( + 0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883 + ) }), - id3: Honk.G1Point({ - x: uint256(0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d), - y: uint256(0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5) + id3: Honk.G1Point({ + x: uint256( + 0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d + ), + y: uint256( + 0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5 + ) }), - id4: Honk.G1Point({ - x: uint256(0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a), - y: uint256(0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02) + id4: Honk.G1Point({ + x: uint256( + 0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a + ), + y: uint256( + 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 + ) }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + lagrangeFirst: Honk.G1Point({ + x: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000001 + ), + y: uint256( + 0x0000000000000000000000000000000000000000000000000000000000000002 + ) }), - lagrangeLast: Honk.G1Point({ - x: uint256(0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400), - y: uint256(0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1) + lagrangeLast: Honk.G1Point({ + x: uint256( + 0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400 + ), + y: uint256( + 0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1 + ) }) }); return vk; @@ -135,24 +251,31 @@ library HonkVerificationKey { pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify( + bytes calldata _proof, + bytes32[] calldata _publicInputs + ) external returns (bool); } type Fr is uint256; -using {add as +} for Fr global; -using {sub as -} for Fr global; -using {mul as *} for Fr global; +using { add as + } for Fr global; +using { sub as - } for Fr global; +using { mul as * } for Fr global; -using {exp as ^} for Fr global; -using {notEqual as !=} for Fr global; -using {equal as ==} for Fr global; +using { exp as ^ } for Fr global; +using { notEqual as != } for Fr global; +using { equal as == } for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order uint256 constant P = MODULUS; -Fr constant SUBGROUP_GENERATOR = Fr.wrap(0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76); -Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6); +Fr constant SUBGROUP_GENERATOR = Fr.wrap( + 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76 +); +Fr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap( + 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6 +); Fr constant MINUS_ONE = Fr.wrap(MODULUS - 1); Fr constant ONE = Fr.wrap(1); Fr constant ZERO = Fr.wrap(0); @@ -298,9 +421,11 @@ uint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9; uint256 constant NUMBER_OF_ENTITIES = 41; // The number of entities added for ZK (gemini_masking_poly) uint256 constant NUM_MASKING_POLYNOMIALS = 1; -uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_OF_ENTITIES_ZK = NUMBER_OF_ENTITIES + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_UNSHIFTED = 36; -uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + NUM_MASKING_POLYNOMIALS; +uint256 constant NUMBER_UNSHIFTED_ZK = NUMBER_UNSHIFTED + + NUM_MASKING_POLYNOMIALS; uint256 constant NUMBER_TO_BE_SHIFTED = 5; uint256 constant PAIRING_POINTS_SIZE = 16; @@ -496,26 +621,63 @@ library ZKTranscriptLib { uint256 logN ) external pure returns (ZKTranscript memory t) { Fr previousChallenge; - (t.relationParameters, previousChallenge) = - generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + ( + t.relationParameters, + previousChallenge + ) = generateRelationParametersChallenges( + proof, + publicInputs, + vkHash, + publicInputsSize, + previousChallenge + ); - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + (t.alphas, previousChallenge) = generateAlphaChallenges( + previousChallenge, + proof + ); - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + (t.gateChallenges, previousChallenge) = generateGateChallenges( + previousChallenge, + logN + ); + (t.libraChallenge, previousChallenge) = generateLibraChallenge( + previousChallenge, + proof + ); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges( + proof, + previousChallenge, + logN + ); - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + (t.rho, previousChallenge) = generateRhoChallenge( + proof, + previousChallenge + ); - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + (t.geminiR, previousChallenge) = generateGeminiRChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge( + proof, + previousChallenge, + logN + ); - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge( + proof, + previousChallenge + ); return t; } - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + function splitChallenge( + Fr challenge + ) internal pure returns (Fr first, Fr second) { uint256 challengeU256 = uint256(Fr.unwrap(challenge)); // Split into two equal 127-bit chunks (254/2) uint256 lo = challengeU256 & 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // 127 bits @@ -530,11 +692,23 @@ library ZKTranscriptLib { uint256 vkHash, uint256 publicInputsSize, Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = - generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + ) + internal + pure + returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) + { + ( + rp.eta, + rp.etaTwo, + rp.etaThree, + previousChallenge + ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + ( + rp.beta, + rp.gamma, + nextPreviousChallenge + ) = generateBetaAndGammaChallenges(previousChallenge, proof); } function generateEtaChallenge( @@ -542,7 +716,11 @@ library ZKTranscriptLib { bytes32[] calldata publicInputs, uint256 vkHash, uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + ) + internal + pure + returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) + { // Size: 1 (vkHash) + publicInputsSize + 8 (geminiMask(2) + 3 wires(6)) bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 8); round0[0] = bytes32(vkHash); @@ -551,7 +729,8 @@ library ZKTranscriptLib { round0[1 + i] = bytes32(publicInputs[i]); } for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib + .toBytes32(proof.pairingPointObject[i]); } // For ZK flavors: hash the gemini masking poly commitment (sent right after public inputs) @@ -567,18 +746,21 @@ library ZKTranscriptLib { round0[1 + publicInputsSize + 6] = bytes32(proof.w3.x); round0[1 + publicInputsSize + 7] = bytes32(proof.w3.y); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round0)) + ); (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); - (etaThree,) = splitChallenge(previousChallenge); + (etaThree, ) = splitChallenge(previousChallenge); } - function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) - { + function generateBetaAndGammaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { bytes32[7] memory round1; round1[0] = FrLib.toBytes32(previousChallenge); round1[1] = bytes32(proof.lookupReadCounts.x); @@ -588,12 +770,17 @@ library ZKTranscriptLib { round1[5] = bytes32(proof.w4.x); round1[6] = bytes32(proof.w4.y); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(round1)) + ); (beta, gamma) = splitChallenge(nextPreviousChallenge); } // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + function generateAlphaChallenges( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) @@ -606,9 +793,11 @@ library ZKTranscriptLib { alpha0[3] = proof.zPerm.x; alpha0[4] = proof.zPerm.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(alpha0)) + ); Fr alpha; - (alpha,) = splitChallenge(nextPreviousChallenge); + (alpha, ) = splitChallenge(nextPreviousChallenge); // Compute powers of alpha for batching subrelations alphas[0] = alpha; @@ -617,38 +806,54 @@ library ZKTranscriptLib { } } - function generateGateChallenges(Fr previousChallenge, uint256 logN) + function generateGateChallenges( + Fr previousChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, + Fr nextPreviousChallenge + ) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0],) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(Fr.unwrap(previousChallenge))) + ); + (gateChallenges[0], ) = splitChallenge(previousChallenge); for (uint256 i = 1; i < logN; i++) { gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; } nextPreviousChallenge = previousChallenge; } - function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) - internal - pure - returns (Fr libraChallenge, Fr nextPreviousChallenge) - { + function generateLibraChallenge( + Fr previousChallenge, + Honk.ZKProof memory proof + ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { // 2 comm, 1 sum, 1 challenge uint256[4] memory challengeData; challengeData[0] = Fr.unwrap(previousChallenge); challengeData[1] = proof.libraCommitments[0].x; challengeData[2] = proof.libraCommitments[0].y; challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(challengeData)) + ); + (libraChallenge, ) = splitChallenge(nextPreviousChallenge); } - function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + function generateSumcheckChallenges( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure - returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) + returns ( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, + Fr nextPreviousChallenge + ) { for (uint256 i = 0; i < logN; i++) { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; @@ -657,24 +862,27 @@ library ZKTranscriptLib { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); + prevChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(univariateChal)) + ); - (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); } nextPreviousChallenge = prevChallenge; } // We add Libra claimed eval + 2 libra commitments (grand_sum, quotient) - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr rho, Fr nextPreviousChallenge) - { + function generateRhoChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr rho, Fr nextPreviousChallenge) { uint256[NUMBER_OF_ENTITIES_ZK + 6] memory rhoChallengeElements; rhoChallengeElements[0] = Fr.unwrap(prevChallenge); uint256 i; for (i = 1; i <= NUMBER_OF_ENTITIES_ZK; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + rhoChallengeElements[i] = Fr.unwrap( + proof.sumcheckEvaluations[i - 1] + ); } rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); i += 1; @@ -684,15 +892,17 @@ library ZKTranscriptLib { rhoChallengeElements[i] = proof.libraCommitments[2].x; rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(rhoChallengeElements)) + ); + (rho, ) = splitChallenge(nextPreviousChallenge); } - function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr geminiR, Fr nextPreviousChallenge) - { + function generateGeminiRChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); gR[0] = Fr.unwrap(prevChallenge); @@ -701,59 +911,77 @@ library ZKTranscriptLib { gR[2 + i * 2] = proof.geminiFoldComms[i].y; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(gR)) + ); - (geminiR,) = splitChallenge(nextPreviousChallenge); + (geminiR, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) - internal - pure - returns (Fr shplonkNu, Fr nextPreviousChallenge) - { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + function generateShplonkNuChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge, + uint256 logN + ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { + uint256[] memory shplonkNuChallengeElements = new uint256[]( + logN + 1 + 4 + ); shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.geminiAEvaluations[i - 1] + ); } uint256 libraIdx = 0; for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + shplonkNuChallengeElements[i] = Fr.unwrap( + proof.libraPolyEvals[libraIdx] + ); libraIdx++; } - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkNuChallengeElements)) + ); + (shplonkNu, ) = splitChallenge(nextPreviousChallenge); } - function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) - internal - pure - returns (Fr shplonkZ, Fr nextPreviousChallenge) - { + function generateShplonkZChallenge( + Honk.ZKProof memory proof, + Fr prevChallenge + ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { uint256[3] memory shplonkZChallengeElements; shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); shplonkZChallengeElements[1] = proof.shplonkQ.x; shplonkZChallengeElements[2] = proof.shplonkQ.y; - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ,) = splitChallenge(nextPreviousChallenge); + nextPreviousChallenge = FrLib.fromBytes32( + keccak256(abi.encodePacked(shplonkZChallengeElements)) + ); + (shplonkZ, ) = splitChallenge(nextPreviousChallenge); } - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + function loadProof( + bytes calldata proof, + uint256 logN + ) internal pure returns (Honk.ZKProof memory p) { uint256 boundary = 0x0; // Pairing point object for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.pairingPointObject[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Gemini masking polynomial commitment (sent first in ZK flavors, right after pairing points) - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiMaskingPoly = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Commitments @@ -765,17 +993,25 @@ library ZKTranscriptLib { boundary += GROUP_ELEMENT_SIZE; // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadCounts = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupReadTags = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.lookupInverses = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[0] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); @@ -783,48 +1019,68 @@ library ZKTranscriptLib { // Sumcheck univariates for (uint256 i = 0; i < logN; i++) { for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckUnivariates[i][j] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } } // Sumcheck evaluations (includes gemini_masking_poly eval at index 0 for ZK flavors) for (uint256 i = 0; i < NUMBER_OF_ENTITIES_ZK; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.sumcheckEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraEvaluation = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[1] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.libraCommitments[2] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // Gemini // Read gemini fold univariates for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.geminiFoldComms[i] = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; } // Read gemini a evaluations for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.geminiAEvaluations[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + p.libraPolyEvals[i] = bytesToFr( + proof[boundary:boundary + FIELD_ELEMENT_SIZE] + ); boundary += FIELD_ELEMENT_SIZE; } // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.shplonkQ = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); boundary += GROUP_ELEMENT_SIZE; // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + p.kzgQuotient = bytesToG1Point( + proof[boundary:boundary + GROUP_ELEMENT_SIZE] + ); } } @@ -842,18 +1098,60 @@ library RelationsLib { Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateArithmeticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePermutationRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateLogDerivativeLookupRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateDeltaRangeRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateEllipticRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulateMemoryRelation( + purportedEvaluations, + rp, + evaluations, + powPartialEval + ); + accumulateNnfRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonExternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); + accumulatePoseidonInternalRelation( + purportedEvaluations, + evaluations, + powPartialEval + ); // batch the subrelations with the precomputed alpha powers to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, subrelationChallenges); + accumulator = scaleAndBatchSubrelations( + evaluations, + subrelationChallenges + ); } /** @@ -861,11 +1159,15 @@ library RelationsLib { * 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; + uint256 internal constant NEG_HALF_MODULO_P = + 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** * Ultra Arithmetic Relation * @@ -881,9 +1183,16 @@ library RelationsLib { { 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); + Fr accum = (q_arith - Fr.wrap(3)) * + (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * + neg_half; + accum = + accum + + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + + wire(p, WIRE.Q_C); accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); accum = accum * q_arith; accum = accum * domainSep; @@ -892,7 +1201,10 @@ library RelationsLib { // Relation 1 { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + Fr accum = wire(p, WIRE.W_L) + + wire(p, WIRE.W_4) - + wire(p, WIRE.W_L_SHIFT) + + wire(p, WIRE.Q_M); accum = accum * (q_arith - Fr.wrap(2)); accum = accum * (q_arith - ONE); accum = accum * q_arith; @@ -911,36 +1223,67 @@ library RelationsLib { Fr grand_product_denominator; { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + Fr num = wire(p, WIRE.W_L) + + wire(p, WIRE.ID_1) * + rp.beta + + rp.gamma; + num = + num * + (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = + num * + (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); grand_product_numerator = num; } { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + Fr den = wire(p, WIRE.W_L) + + wire(p, WIRE.SIGMA_1) * + rp.beta + + rp.gamma; + den = + den * + (wire(p, WIRE.W_R) + + wire(p, WIRE.SIGMA_2) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_O) + + wire(p, WIRE.SIGMA_3) * + rp.beta + + rp.gamma); + den = + den * + (wire(p, WIRE.W_4) + + wire(p, WIRE.SIGMA_4) * + rp.beta + + rp.gamma); grand_product_denominator = den; } // Contribution 2 { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; - - acc = acc - - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) - * grand_product_denominator); + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * + grand_product_numerator; + + acc = + acc - + ((wire(p, WIRE.Z_PERM_SHIFT) + + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * + grand_product_denominator); acc = acc * domainSep; evals[2] = acc; } // Contribution 3 { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * + wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; evals[3] = acc; } } @@ -956,33 +1299,52 @@ library RelationsLib { // Calculate the write term (the table accumulation) { - write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) - + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + write_term = + wire(p, WIRE.TABLE_1) + + rp.gamma + + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + + (wire(p, WIRE.TABLE_4) * rp.etaThree); } // Calculate the write term { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) - + (wire(p, WIRE.Q_O) * rp.etaThree); + Fr derived_entry_1 = wire(p, WIRE.W_L) + + rp.gamma + + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + + wire(p, WIRE.Q_M) * + wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + + wire(p, WIRE.Q_C) * + wire(p, WIRE.W_O_SHIFT); + + read_term = + derived_entry_1 + + (derived_entry_2 * rp.eta) + + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); } Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - Fr inverse_exists_xor = - wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + + wire(p, WIRE.Q_LOOKUP) - + (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + Fr accumulatorNone = read_term * + write_term * + wire(p, WIRE.LOOKUP_INVERSES) - + inverse_exists_xor; accumulatorNone = accumulatorNone * domainSep; // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * + read_inverse - + wire(p, WIRE.LOOKUP_READ_COUNTS) * + write_inverse; Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); @@ -1096,7 +1458,11 @@ library RelationsLib { x_add_identity = x_add_identity * x_diff * x_diff; x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + evals[11] = + x_add_identity * + partialEval * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 11 point addition, x-coordinate check @@ -1104,8 +1470,15 @@ library RelationsLib { { Fr y1_plus_y3 = ep.y_1 + ep.y_3; Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + Fr y_add_identity = y1_plus_y3 * + x_diff + + (ep.x_3 - ep.x_1) * + y_diff; + evals[12] = + y_add_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + (ONE - q_is_double); } // Contribution 10 point doubling, x-coordinate check @@ -1118,9 +1491,15 @@ library RelationsLib { Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; - - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + ep.x_double_identity = + (ep.x_3 + ep.x_1 + ep.x_1) * + y1_sqr_mul_4 - + x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; evals[11] = evals[11] + acc; } @@ -1128,8 +1507,16 @@ library RelationsLib { // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 { Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + Fr y_double_identity = x1_sqr_mul_3 * + (ep.x_1 - ep.x_3) - + (ep.y_1 + ep.y_1) * + (ep.y_1 + ep.y_3); + evals[12] = + evals[12] + + y_double_identity * + domainSep * + wire(p, WIRE.Q_ELLIPTIC) * + q_is_double; } } @@ -1203,8 +1590,12 @@ library RelationsLib { * For ROM gates, qc = 0 */ ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = + ap.memory_record_check + + (wire(p, WIRE.W_L) * rp.eta); ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); @@ -1228,16 +1619,26 @@ library RelationsLib { ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + ap.index_is_monotonically_increasing = + ap.index_delta * + (ap.index_delta - Fr.wrap(1)); // deg 2 - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + ap.adjacent_values_match_if_adjacent_indices_match = + (ap.index_delta * MINUS_ONE + ONE) * + ap.record_delta; // deg 2 - evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) - * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[14] = + ap.adjacent_values_match_if_adjacent_indices_match * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * + (wire(p, WIRE.Q_MEMORY) * 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 @@ -1264,13 +1665,22 @@ library RelationsLib { // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta // deg 1 or 4 ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = + ap.next_gate_access_type + + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = + wire(p, WIRE.W_4_SHIFT) - + ap.next_gate_access_type; Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * + value_delta * + (ap.next_gate_access_type * MINUS_ONE + ONE); // 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 @@ -1278,15 +1688,28 @@ library RelationsLib { // type is correct, to cover this edge case // deg 2 or 4 ap.next_gate_access_type_is_boolean = - ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + ap.next_gate_access_type * + ap.next_gate_access_type - + ap.next_gate_access_type; // Putting it all together... - evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation - * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + evals[16] = + ap + .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = + ap.index_is_monotonically_increasing * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = + ap.next_gate_access_type_is_boolean * + (wire(p, WIRE.Q_O)) * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = + ap.access_check * + (wire(p, WIRE.Q_O)); // deg 3 or 9 /** * RAM Timestamp Consistency Check @@ -1300,7 +1723,10 @@ library RelationsLib { * Else timestamp_check = 0 */ ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + ap.RAM_timestamp_check_identity = + (ap.index_delta * MINUS_ONE + ONE) * + ap.timestamp_delta - + wire(p, WIRE.W_O); // deg 3 /** * Complete Contribution 12 @@ -1309,12 +1735,21 @@ 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.RAM_timestamp_check_identity * + (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = + ap.memory_identity + + ap.memory_record_check * + (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + + ap.RAM_consistency_check_identity; // deg 3 or 9 // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + ap.memory_identity = + ap.memory_identity * + (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 evals[13] = ap.memory_identity; } @@ -1353,28 +1788,56 @@ library RelationsLib { * * */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = - (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.limb_subproduct = + wire(p, WIRE.W_L) * + wire(p, WIRE.W_R_SHIFT) + + wire(p, WIRE.W_L_SHIFT) * + wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * + wire(p, WIRE.W_4) + + wire(p, WIRE.W_R) * + wire(p, WIRE.W_O) - + wire(p, WIRE.W_O_SHIFT)); ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 - + wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 + + ap.limb_subproduct; + ap.non_native_field_gate_2 = + ap.non_native_field_gate_2 * + wire(p, WIRE.Q_4); ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.limb_subproduct = + ap.limb_subproduct + + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 - + (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = + ap.non_native_field_gate_1 * + wire(p, WIRE.Q_O); ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = - ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 + + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 - + (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = + ap.non_native_field_gate_3 * + wire(p, WIRE.Q_M); + + Fr non_native_field_identity = ap.non_native_field_gate_1 + + ap.non_native_field_gate_2 + + ap.non_native_field_gate_3; + non_native_field_identity = + non_native_field_identity * + wire(p, WIRE.Q_R); // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm // deg 2 @@ -1402,8 +1865,11 @@ library RelationsLib { ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + Fr limb_accumulator_identity = ap.limb_accumulator_1 + + ap.limb_accumulator_2; + limb_accumulator_identity = + limb_accumulator_identity * + wire(p, WIRE.Q_O); // deg 3 ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); @@ -1463,13 +1929,25 @@ library RelationsLib { ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[20] = + evals[20] + + ep.q_pos_by_scaling * + (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[21] = + evals[21] + + ep.q_pos_by_scaling * + (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[22] = + evals[22] + + ep.q_pos_by_scaling * + (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[23] = + evals[23] + + ep.q_pos_by_scaling * + (ep.v4 - wire(p, WIRE.W_4_SHIFT)); } struct PoseidonInternalParams { @@ -1494,10 +1972,18 @@ library RelationsLib { PoseidonInternalParams memory ip; Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + FrLib.from( + 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7 + ), + FrLib.from( + 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b + ), + FrLib.from( + 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15 + ), + FrLib.from( + 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b + ) ]; // add round constants @@ -1515,16 +2001,28 @@ library RelationsLib { ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + evals[24] = + evals[24] + + ip.q_pos_by_scaling * + (ip.v1 - wire(p, WIRE.W_L_SHIFT)); ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + evals[25] = + evals[25] + + ip.q_pos_by_scaling * + (ip.v2 - wire(p, WIRE.W_R_SHIFT)); ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + evals[26] = + evals[26] + + ip.q_pos_by_scaling * + (ip.v3 - wire(p, WIRE.W_O_SHIFT)); ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + evals[27] = + evals[27] + + ip.q_pos_by_scaling * + (ip.v4 - wire(p, WIRE.W_4_SHIFT)); } // Batch subrelation evaluations using precomputed powers of alpha @@ -1536,7 +2034,10 @@ library RelationsLib { accumulator = evaluations[0]; for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + accumulator = + accumulator + + evaluations[i] * + subrelationChallenges[i - 1]; } } } @@ -1572,7 +2073,10 @@ library CommitmentSchemeLib { Fr[] foldPosEvaluations; } - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + function computeSquares( + Fr r, + uint256 logN + ) internal pure returns (Fr[] memory) { Fr[] memory squares = new Fr[](logN); squares[0] = r; for (uint256 i = 1; i < logN; ++i) { @@ -1594,10 +2098,15 @@ library CommitmentSchemeLib { Fr challengePower = geminiEvalChallengePowers[i - 1]; Fr u = sumcheckUChallenges[i - 1]; - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - geminiEvaluations[i - 1] - * (challengePower * (ONE - u) - u)); + Fr batchedEvalRoundAcc = ((challengePower * + batchedEvalAccumulator * + Fr.wrap(2)) - + geminiEvaluations[i - 1] * + (challengePower * (ONE - u) - u)); // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + batchedEvalRoundAcc = + batchedEvalRoundAcc * + (challengePower * (ONE - u) + u).invert(); batchedEvalAccumulator = batchedEvalRoundAcc; foldPosEvaluations[i - 1] = batchedEvalRoundAcc; @@ -1628,13 +2137,18 @@ function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { } // EC Point utilities -function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { +function bytesToG1Point( + bytes calldata proofSection +) pure returns (Honk.G1Point memory point) { point = Honk.G1Point({ - x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); } -function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { +function negateInplace( + Honk.G1Point memory point +) pure returns (Honk.G1Point memory) { point.y = (Q - point.y) % Q; return point; } @@ -1651,10 +2165,9 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) - pure - returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) -{ +function convertPairingPointsToG1( + Fr[PAIRING_POINTS_SIZE] memory pairingPoints +) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { uint256 lhsX = Fr.unwrap(pairingPoints[0]); lhsX |= Fr.unwrap(pairingPoints[1]) << 68; lhsX |= Fr.unwrap(pairingPoints[2]) << 136; @@ -1698,7 +2211,10 @@ function generateRecursionSeparator( // hash the accum X // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + ( + Honk.G1Point memory proofLhs, + Honk.G1Point memory proofRhs + ) = convertPairingPointsToG1(proofPairingPoints); uint256[8] memory recursionSeparatorElements; @@ -1714,7 +2230,9 @@ function generateRecursionSeparator( recursionSeparatorElements[6] = accRhs.x; recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32( + keccak256(abi.encodePacked(recursionSeparatorElements)) + ); } /** @@ -1726,10 +2244,11 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) - view - returns (Honk.G1Point memory) -{ +function mulWithSeperator( + Honk.G1Point memory basePoint, + Honk.G1Point memory other, + Fr recursionSeperator +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; result = ecMul(recursionSeperator, basePoint); @@ -1746,7 +2265,10 @@ function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory oth * @param point The point to multiply. * @return result The result of the multiplication. */ -function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { +function ecMul( + Fr value, + Honk.G1Point memory point +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1792,7 +2314,10 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @param rhs The right hand side of the addition. * @return result The result of the addition. */ -function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { +function ecAdd( + Honk.G1Point memory lhs, + Honk.G1Point memory rhs +) view returns (Honk.G1Point memory) { Honk.G1Point memory result; assembly { @@ -1816,7 +2341,9 @@ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (H // Call the ecAdd precompile, it takes in the following // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { revert(0, 0) } + if iszero(success) { + revert(0, 0) + } // Copy the result of the addition back into the result memory location. // Memory layout: @@ -1845,22 +2372,41 @@ function validateOnCurve(Honk.G1Point memory point) pure { require(success, "point is not on the curve"); } -function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { +function pairing( + Honk.G1Point memory rhs, + Honk.G1Point memory lhs +) view returns (bool decodedResult) { bytes memory input = abi.encodePacked( rhs.x, rhs.y, // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + uint256( + 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 + ), + uint256( + 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed + ), + uint256( + 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b + ), + uint256( + 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + ), lhs.x, lhs.y, // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + uint256( + 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1 + ), + uint256( + 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 + ), + uint256( + 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4 + ), + uint256( + 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 + ) ); (bool success, bytes memory result) = address(0x08).staticcall(input); @@ -1869,9 +2415,6 @@ function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns // Field arithmetic libraries - prevent littering the code with modmul / addmul - - - abstract contract BaseZKHonkVerifier is IVerifier { using FrLib for Fr; @@ -1881,7 +2424,12 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 immutable $NUM_PUBLIC_INPUTS; uint256 immutable $MSMSize; - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + constructor( + uint256 _N, + uint256 _logN, + uint256 _vkHash, + uint256 _numPublicInputs + ) { $N = _N; $LOG_N = _logN; $VK_HASH = _vkHash; @@ -1891,7 +2439,11 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Errors error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error ProofLengthWrongWithLogN( + uint256 logN, + uint256 actualLength, + uint256 expectedLength + ); error PublicInputsLengthWrong(); error SumcheckFailed(); error ShpleminiFailed(); @@ -1911,7 +2463,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { proofLength += NUM_ELEMENTS_COMM * 3; // Libra concat, grand sum, quotient comms + Gemini masking // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += + logN * + ZK_BATCHED_RELATION_PARTIAL_LENGTH * + NUM_ELEMENTS_FR; // sumcheck univariates proofLength += NUMBER_OF_ENTITIES_ZK * NUM_ELEMENTS_FR; // sumcheck evaluations // Libra and Gemini @@ -1931,20 +2486,26 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 constant SHIFTED_COMMITMENTS_START = 30; - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function loadVerificationKey() + internal + pure + virtual + returns (Honk.VerificationKey memory); - function verify(bytes calldata proof, bytes32[] calldata publicInputs) - public - view - override - returns (bool verified) - { + function verify( + bytes calldata proof, + bytes32[] calldata publicInputs + ) public view override returns (bool verified) { // Calculate expected proof size based on $LOG_N uint256 expectedProofSize = calculateProofSize($LOG_N); // Check the received proof is the expected size where each field element is 32 bytes if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + revert ProofLengthWrongWithLogN( + $LOG_N, + proof.length, + expectedProofSize * 32 + ); } Honk.VerificationKey memory vk = loadVerificationKey(); @@ -1955,15 +2516,20 @@ abstract contract BaseZKHonkVerifier is IVerifier { } // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = - ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + ZKTranscript memory t = ZKTranscriptLib.generateTranscript( + p, + publicInputs, + $VK_HASH, + $NUM_PUBLIC_INPUTS, + $LOG_N + ); // Derive public input delta t.relationParameters.publicInputsDelta = computePublicInputDelta( publicInputs, p.pairingPointObject, t.relationParameters.beta, - t.relationParameters.gamma, /*pubInputsOffset=*/ + t.relationParameters.gamma /*pubInputsOffset=*/, 1 ); @@ -1987,11 +2553,16 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr numerator = Fr.wrap(1); Fr denominator = Fr.wrap(1); - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr numeratorAcc = gamma + + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + for ( + uint256 i = 0; + i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; + i++ + ) { Fr pubInput = FrLib.fromBytes32(publicInputs[i]); numerator = numerator * (numeratorAcc + pubInput); @@ -2016,22 +2587,32 @@ abstract contract BaseZKHonkVerifier is IVerifier { publicInputDelta = FrLib.div(numerator, denominator); } - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + function verifySumcheck( + Honk.ZKProof memory proof, + ZKTranscript memory tp + ) internal view returns (bool verified) { Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 Fr powPartialEvaluation = Fr.wrap(1); // We perform sumcheck reductions over log n rounds ( the multivariate degree ) for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory roundUnivariate = proof.sumcheckUnivariates[round]; Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; if (totalSum != roundTargetSum) revert SumcheckFailed(); Fr roundChallenge = tp.sumCheckUChallenges[round]; // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + roundTargetSum = computeNextTargetSum( + roundUnivariate, + roundChallenge + ); powPartialEvaluation = - powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + powPartialEvaluation * + (Fr.wrap(1) + + roundChallenge * + (tp.gateChallenges[round] - Fr.wrap(1))); } // Last round @@ -2039,10 +2620,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Index 0 is gemini_masking_poly, indices 1-41 are the regular entities used in relations Fr[NUMBER_OF_ENTITIES] memory relationsEvaluations; for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - relationsEvaluations[i] = proof.sumcheckEvaluations[i + NUM_MASKING_POLYNOMIALS]; // Skip gemini_masking_poly at index 0 + relationsEvaluations[i] = proof.sumcheckEvaluations[ + i + NUM_MASKING_POLYNOMIALS + ]; // Skip gemini_masking_poly at index 0 } Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - relationsEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + relationsEvaluations, + tp.relationParameters, + tp.alphas, + powPartialEvaluation ); Fr evaluation = Fr.wrap(1); @@ -2051,27 +2637,48 @@ abstract contract BaseZKHonkVerifier is IVerifier { } grandHonkRelationSum = - grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + grandHonkRelationSum * + (Fr.wrap(1) - evaluation) + + proof.libraEvaluation * + tp.libraChallenge; verified = (grandHonkRelationSum == roundTargetSum); } // Return the new target sum for the next sumcheck round - function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) - internal - view - returns (Fr targetSum) - { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; + function computeNextTargetSum( + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, + Fr roundChallenge + ) internal view returns (Fr targetSum) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] + memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000000240 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31 + ), + Fr.wrap( + 0x00000000000000000000000000000000000000000000000000000000000005a0 + ), + Fr.wrap( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51 + ), + Fr.wrap( + 0x0000000000000000000000000000000000000000000000000000000000009d80 + ) + ]; // To compute the next target sum, we evaluate the given univariate at a point u (challenge). @@ -2084,11 +2691,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + denominatorInverses[i] = FrLib.invert( + BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * + (roundChallenge - Fr.wrap(i)) + ); } for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + targetSum = + targetSum + + roundUnivariates[i] * + denominatorInverses[i]; } // Scale the sum by the value of B(x) @@ -2104,56 +2717,63 @@ abstract contract BaseZKHonkVerifier is IVerifier { Honk.G1Point P_1; } - function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) - internal - view - returns (bool verified) - { + function verifyShplemini( + Honk.ZKProof memory proof, + Honk.VerificationKey memory vk, + ZKTranscript memory tp + ) internal view returns (bool verified) { CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib + .computeSquares(tp.geminiR, $LOG_N); // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings Fr[] memory scalars = new Fr[]($MSMSize); Honk.G1Point[] memory commitments = new Honk.G1Point[]($MSMSize); - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[0]).invert(); - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.unshiftedScalar = + mem.posInvertedDenominator + + (tp.shplonkNu * mem.negInvertedDenominator); mem.shiftedScalar = - tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + tp.geminiR.invert() * + (mem.posInvertedDenominator - + (tp.shplonkNu * mem.negInvertedDenominator)); scalars[0] = Fr.wrap(1); commitments[0] = 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$. - */ + * 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$. + */ // For ZK flavors: evaluations array is [gemini_masking_poly, qm, qc, ql, qr, ...] // Start batching challenge at 1, not rho, to match non-ZK pattern mem.batchingChallenge = Fr.wrap(1); @@ -2165,8 +2785,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Process all NUMBER_UNSHIFTED_ZK evaluations (includes gemini_masking_poly at index 0) for (uint256 i = 1; i <= NUMBER_UNSHIFTED_ZK; ++i) { scalars[i] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation - + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + + (proof.sumcheckEvaluations[i - NUM_MASKING_POLYNOMIALS] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } // g commitments are accumulated at r @@ -2179,9 +2801,13 @@ abstract contract BaseZKHonkVerifier is IVerifier { uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; uint256 evaluationOff = i + NUMBER_UNSHIFTED_ZK; - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + scalars[scalarOff] = + scalars[scalarOff] + + (mem.shiftedScalarNeg * mem.batchingChallenge); mem.batchedEvaluation = - mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchedEvaluation + + (proof.sumcheckEvaluations[evaluationOff] * + mem.batchingChallenge); mem.batchingChallenge = mem.batchingChallenge * tp.rho; } @@ -2234,15 +2860,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { * \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: + * 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] @@ -2251,17 +2877,23 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + Fr[] memory foldPosEvaluations = CommitmentSchemeLib + .computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; mem.constantTermAccumulator = - mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + foldPosEvaluations[0] * + mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + (proof.geminiAEvaluations[0] * + tp.shplonkNu * + mem.negInvertedDenominator); mem.batchingChallenge = tp.shplonkNu.sqr(); uint256 boundary = NUMBER_UNSHIFTED_ZK + 1; @@ -2273,22 +2905,40 @@ abstract contract BaseZKHonkVerifier is IVerifier { if (!dummy_round) { // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + mem.posInvertedDenominator = (tp.shplonkZ - + powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + + powers_of_evaluation_challenge[i + 1]).invert(); // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + mem.scalingFactorPos = + mem.batchingChallenge * + mem.posInvertedDenominator; + mem.scalingFactorNeg = + mem.batchingChallenge * + tp.shplonkNu * + mem.negInvertedDenominator; + scalars[boundary + i] = + mem.scalingFactorNeg.neg() + + mem.scalingFactorPos.neg(); // Accumulate the const term contribution given by // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + Fr accumContribution = mem.scalingFactorNeg * + proof.geminiAEvaluations[i + 1]; + accumContribution = + accumContribution + + mem.scalingFactorPos * + foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + accumContribution; } // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; commitments[boundary + i] = proof.geminiFoldComms[i]; } @@ -2297,16 +2947,24 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Finalize the batch opening claim mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div( + tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR + ); mem.denominators[2] = mem.denominators[0]; mem.denominators[3] = mem.denominators[0]; - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + mem.batchingChallenge = + mem.batchingChallenge * + tp.shplonkNu * + tp.shplonkNu; for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; mem.batchingScalars[i] = scalingFactor.neg(); mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + mem.constantTermAccumulator = + mem.constantTermAccumulator + + scalingFactor * + proof.libraPolyEvals[i]; } scalars[boundary] = mem.batchingScalars[0]; scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; @@ -2316,10 +2974,17 @@ abstract contract BaseZKHonkVerifier is IVerifier { commitments[boundary++] = proof.libraCommitments[i]; } - commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); scalars[boundary++] = mem.constantTermAccumulator; - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + if ( + !checkEvalsConsistency( + proof.libraPolyEvals, + tp.geminiR, + tp.sumCheckUChallenges, + proof.libraEvaluation + ) + ) { revert ConsistencyCheckFailed(); } @@ -2333,9 +2998,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { pair.P_1 = negateInplace(quotient_commitment); // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = - convertPairingPointsToG1(proof.pairingPointObject); + Fr recursionSeparator = generateRecursionSeparator( + proof.pairingPointObject, + pair.P_0, + pair.P_1 + ); + ( + Honk.G1Point memory P_0_other, + Honk.G1Point memory P_1_other + ) = convertPairingPointsToG1(proof.pairingPointObject); // Validate the points from the proof are on the curve validateOnCurve(P_0_other); @@ -2375,8 +3046,14 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 round = 0; round < $LOG_N; round++) { uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + for ( + uint256 idx = currIdx + 1; + idx < currIdx + LIBRA_UNIVARIATES_LENGTH; + idx++ + ) { + mem.challengePolyLagrange[idx] = + mem.challengePolyLagrange[idx - 1] * + uChallenges[round]; } } @@ -2385,7 +3062,10 @@ abstract contract BaseZKHonkVerifier is IVerifier { for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { mem.denominators[idx] = mem.rootPower * geminiR - one; mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.challengePolyEval = + mem.challengePolyEval + + mem.challengePolyLagrange[idx] * + mem.denominators[idx]; mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; } @@ -2396,19 +3076,28 @@ abstract contract BaseZKHonkVerifier is IVerifier { mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - mem.diff = mem.diff + (geminiR - SUBGROUP_GENERATOR_INVERSE) - * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + mem.diff = + mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) * + (libraPolyEvals[1] - + libraPolyEvals[2] - + libraPolyEvals[0] * + mem.challengePolyEval); + mem.diff = + mem.diff + + mem.lagrangeLast * + (libraPolyEvals[2] - libraEval) - + vanishingPolyEval * + libraPolyEvals[3]; check = mem.diff == Fr.wrap(0); } // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) - internal - view - returns (Honk.G1Point memory result) - { + function batchMul( + Honk.G1Point[] memory base, + Fr[] memory scalars + ) internal view returns (Honk.G1Point memory result) { uint256 limit = $MSMSize; // Validate all points are on the curve @@ -2421,7 +3110,9 @@ abstract contract BaseZKHonkVerifier is IVerifier { let free := mload(0x40) let count := 0x01 - for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + for {} lt(count, add(limit, 1)) { + count := add(count, 1) + } { // Get loop offsets let base_base := add(base, mul(count, 0x20)) let scalar_base := add(scalars, mul(count, 0x20)) @@ -2431,9 +3122,22 @@ abstract contract BaseZKHonkVerifier is IVerifier { // Add scalar mstore(add(free, 0x80), mload(scalar_base)) - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(free, 0x40), + 0x60, + add(free, 0x40), + 0x40 + ) + ) // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + success := and( + success, + staticcall(gas(), 6, free, 0x80, free, 0x40) + ) } // Return the result @@ -2445,8 +3149,15 @@ abstract contract BaseZKHonkVerifier is IVerifier { } } -contract DkgAggregatorVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); +contract DkgAggregatorVerifier is + BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) +{ + function loadVerificationKey() + internal + pure + override + returns (Honk.VerificationKey memory) + { + return HonkVerificationKey.loadVerificationKey(); } } From 97fcc5b247c30649ad5e5aeba92166317033dda3 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 12:10:37 +0200 Subject: [PATCH 15/54] dkg fold attestation, slashing integration and witness test --- Cargo.lock | 1 + .../results_insecure/crisp_verify_gas.json | 78 +++---- .../results_insecure/integration_summary.json | 100 ++++----- .../benchmarks/results_insecure/report.md | 88 ++++---- crates/aggregator/Cargo.toml | 1 + crates/aggregator/src/publickey_aggregator.rs | 158 ++++++++++++++- .../src/enclave_event/dkg_fold_attestation.rs | 123 +++++++++++ .../dkg_recursive_aggregation_complete.rs | 21 +- crates/events/src/enclave_event/mod.rs | 2 + crates/evm/src/slashing_manager_sol_writer.rs | 2 +- crates/zk-prover/scripts/build_fixtures.sh | 8 + crates/zk-prover/src/actors/mod.rs | 4 +- .../src/actors/node_proof_aggregator.rs | 109 +++++++++- crates/zk-prover/src/lib.rs | 2 + crates/zk-prover/src/node_fold_public.rs | 71 +++++++ crates/zk-prover/src/witness.rs | 2 +- crates/zk-prover/tests/fixtures/dummy.json | 22 ++ .../tests/slashing_integration_tests.rs | 191 +++++++++++++----- .../bfv_vk_binding/folded_artifacts.json | 8 +- 19 files changed, 787 insertions(+), 204 deletions(-) create mode 100644 crates/events/src/enclave_event/dkg_fold_attestation.rs create mode 100644 crates/zk-prover/src/node_fold_public.rs create mode 100644 crates/zk-prover/tests/fixtures/dummy.json diff --git a/Cargo.lock b/Cargo.lock index aade891fb..14903ce4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3065,6 +3065,7 @@ dependencies = [ "e3-trbfv", "e3-utils", "e3-zk-helpers", + "e3-zk-prover", "fhe-math", "futures", "num-bigint", diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json index 4fe6bf2c6..e5c316eae 100644 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure/crisp_verify_gas.json @@ -1,8 +1,8 @@ { "verify_gas": { - "dkg": 3042430, - "user": 2972893, - "dec": 3553544 + "dkg": 3042552, + "user": 2973037, + "dec": 3553605 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,60 +17,60 @@ }, "calldata_gas": { "dkg": { - "proof": 169968, + "proof": 170088, "public_inputs": 6144, - "total": 176112 + "total": 176232 }, "dec": { - "proof": 169848, + "proof": 169908, "public_inputs": 17304, - "total": 187152 + "total": 187212 } }, "integration_summary": { "integration_test": "test_trbfv_actor", "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.111521499, "runs": 3, "total_seconds": 0.334564499 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.610321888, "runs": 3, "total_seconds": 1.830965666 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.559050209, "runs": 1, "total_seconds": 0.559050209 }, - { "name": "GenEsiSss", "avg_seconds": 0.124032833, "runs": 3, "total_seconds": 0.372098501 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.226692527, "runs": 3, "total_seconds": 0.680077583 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.500284375, "runs": 1, "total_seconds": 8.500284375 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 49.366586083, "runs": 1, "total_seconds": 49.366586083 }, - { "name": "ZkDkgAggregation", "avg_seconds": 21.116986167, "runs": 1, "total_seconds": 21.116986167 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.465883083, "runs": 6, "total_seconds": 8.795298501 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 62.328322972, "runs": 3, "total_seconds": 186.984968916 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.200691667, "runs": 1, "total_seconds": 2.200691667 }, - { "name": "ZkPkBfv", "avg_seconds": 0.336088264, "runs": 3, "total_seconds": 1.008264792 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.351367042, "runs": 3, "total_seconds": 4.054101126 }, - { "name": "ZkShareComputation", "avg_seconds": 2.682164854, "runs": 6, "total_seconds": 16.092989126 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.506225536, "runs": 24, "total_seconds": 60.149412873 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.176445291, "runs": 3, "total_seconds": 18.529335875 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.100550749, "runs": 3, "total_seconds": 0.301652249 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.221828033, "runs": 5, "total_seconds": 1.109140168 } + { "name": "CalculateDecryptionKey", "avg_seconds": 0.113595944, "runs": 3, "total_seconds": 0.340787834 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.606440319, "runs": 3, "total_seconds": 1.819320959 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.558063459, "runs": 1, "total_seconds": 0.558063459 }, + { "name": "GenEsiSss", "avg_seconds": 0.124646278, "runs": 3, "total_seconds": 0.373938834 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.227111444, "runs": 3, "total_seconds": 0.681334333 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.433242125, "runs": 1, "total_seconds": 8.433242125 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 49.593938792, "runs": 1, "total_seconds": 49.593938792 }, + { "name": "ZkDkgAggregation", "avg_seconds": 20.539657042, "runs": 1, "total_seconds": 20.539657042 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.48974143, "runs": 6, "total_seconds": 8.938448582 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 62.378365152, "runs": 3, "total_seconds": 187.135095458 }, + { "name": "ZkPkAggregation", "avg_seconds": 2.134972792, "runs": 1, "total_seconds": 2.134972792 }, + { "name": "ZkPkBfv", "avg_seconds": 0.340530111, "runs": 3, "total_seconds": 1.021590333 }, + { "name": "ZkPkGeneration", "avg_seconds": 1.378689777, "runs": 3, "total_seconds": 4.136069333 }, + { "name": "ZkShareComputation", "avg_seconds": 2.756735736, "runs": 6, "total_seconds": 16.540414418 }, + { "name": "ZkShareEncryption", "avg_seconds": 2.547185821, "runs": 24, "total_seconds": 61.132459711 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.14141, "runs": 3, "total_seconds": 18.42423 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.097622333, "runs": 3, "total_seconds": 0.292867001 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.228617342, "runs": 5, "total_seconds": 1.14308671 } ], - "operation_timings_total_seconds": 381.986468376, + "operation_timings_total_seconds": 383.239517716, "timings_seconds": [ { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.0715085 }, - { "label": "Committee Setup Completed", "seconds": 20.22829075 }, - { "label": "Committee Finalization Complete", "seconds": 0.006707541 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 304.143739958 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 306.698077958 }, - { "label": "Application CT Gen", "seconds": 0.313315125 }, - { "label": "Running FHE Application", "seconds": 0.003688125 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 79.884513625 }, - { "label": "Entire Test", "seconds": 410.2113715 } + { "label": "Setup completed", "seconds": 3.049949667 }, + { "label": "Committee Setup Completed", "seconds": 20.248450958 }, + { "label": "Committee Finalization Complete", "seconds": 0.005924208 }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 305.202475458 }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 307.797721583 }, + { "label": "Application CT Gen", "seconds": 0.3082625 }, + { "label": "Running FHE Application", "seconds": 0.003257541 }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 79.923254834 }, + { "label": "Entire Test", "seconds": 411.343670791 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x000000000000000000000000000000000000000000000003f7062d91e599a1d9000000000000000000000000000000000000000000000008cc7102c928c65c450000000000000000000000000000000000000000000000075ba3dc02d69ce9910000000000000000000000000000000000000000000000000000ed71a67536b000000000000000000000000000000000000000000000000e44e8edaa774d8d700000000000000000000000000000000000000000000000069df7c477ac356a5a00000000000000000000000000000000000000000000000546c35bc9a1e69c5500000000000000000000000000000000000000000000000000021ee487cecdf100000000000000000000000000000000000000000000000ab881c361ac7b250800000000000000000000000000000000000000000000000615484820700c856a00000000000000000000000000000000000000000000000bb5ddeeb0299d72f60000000000000000000000000000000000000000000000000000614a8dc34464000000000000000000000000000000000000000000000008f86b47c227146d86000000000000000000000000000000000000000000000005b0124c6d784e70a800000000000000000000000000000000000000000000000c7ce22f6009a0150300000000000000000000000000000000000000000000000000028c352ad51c1f0a1e934d28e26ddbc2239f70f1f2a218f011eacde911ac55ca4f56e4c92826ba0151ec8baa1515407b51df92e0368977054f4ff1d48781d8c1bc7962286ed7d20c520eaffc4f8e6351751bd8fe1e162471d0fe3163d177d0ead3ac22d2519a17089d86113a4a8393c93b80f3bb7c9dd5bab24c560c9f0d8906322ab4d14bdf2527575e8ae48fb40af0adc9c7df54fcb65138a862afbaf04d592215d462bcd29107c4ee919449aaed15296c3db7bc5d246d1753696a3fb0d71ae9e02085318c402afd41a4633e857acd0967c13bf26c50c6457ab5dd898ac1864799b9730689d52ad93c530511536a3714b5748aa649245ab18e286181818ec79f628930d4b39d1af275a9bbc79f11513fc819f8a77429a99f5c19dbc3aaa699028fd84578ae771d2cb42f9080e97577d5433096dc7b5872c8a57058cf2b02f8c0e531169ac86d010bdbafbdca84bfb0e04bad65fe3350d55fbfd17c3806f415f2fef12c85a5cb05693555777afb1ed8c9e98ab2221191471b57f70880883d7f77cf7774d4fa8726d1e7542a8d79306c76a221eaf68d96dcc25f7bfa67547c7e7e6823a8f5036a0520516bf26d60b94a8dc1de281b183f97c760db8ac7dd87c986a7ab87c57a3521148a502af09952b50e41ce4798bccddf61aefc5c08cbe35204f43d0c36954a0e89229a121eb3d18ac0c9561aed66af56e683b1ba590eaa4edc1d6476c2762208096dced400b6f35fcbf000acf60ad54d9076eec61d80121dfff0afe26e749c2c7dc41f5ec8f3774ba1084aa7c4d494798c31c4b33f0371083a13e67aaaa9fb297e0ddb44886687d5d35b7485cb47105303ab9d1a237f8dd9dcb93298077c2c1bf65619bd87d561ea96e41c5106896c2a1307eae3afc8ce2c501e6ae5183d9b302f94e692484cba7c25419a42dac6badcb139955e149eccdee3c1b2bbb39bd92006d24ae9e0b0607f5c1994f5c0eec60fdd95dcd71c256fde7e12934ae95ff62c1c3afe38e4250234c03b3fbe574ebdc4b6acce335cffc79409260039c2c5890f33e7fc5c1904725888d81fbb00dfe5829b22b86c60cc60282639614406840619a2a11d45659615c74b56c03d94902360a16b8813b318f90cfdf6d941bd27c11c2c40020df0c8df1b5f56cf8faf6e3f78bdd34f134ab0857d93cc7fc0d8d26a084aeb7c91a1d79ae529ded891a7a2a94b774927a6747644bc87afa45c115cef199c133466b246d8a8f6a87f50454203689ffe4a63c722e36cd714d94d5d8ec715203e52c6915f66cbd5ef50437c703b5c3393cc645517d6c2a210f9ac23d88a2d480d46aa3febce868435cb4052c6af52d836aa60a7141cd972100fdea65c6623f51577a4fdabe52375af64863d09b294de12c786066a4510cf18737b5cb2521bbd58b29c859db5be07fa8057c61e27972541464105d7346270eb1eaf8183f00b03e6634f391192f98965c1fe150acaf8decb6acd8ae9578aba334c8ceb816f220f0a141a0b9c666e712d8917ceae41edfa816a4a3cc319116df04818fed2c5199163f3eb27e7de5e4d47b7a22a1e7e7186dc3a4ec5744e6b26dc0ac4fe236317e300bfa4ac8d1c52a22a867753dbec2a0ef3ea56b28a978dc21c6d56e0a9822cab56055b0a192a4f2a1fa9a1e2ad0d98fb85051bad9fcefaff281aeb31781b070f5b929a644a885cc948d2b8e9b7948b588a829b836bd61f9087f753533e5c0e79b5fc84e0de7671e00294fc3a78e2ad1e7d469a0e2cbc379f6494954a946d23e35a296d5c94f4cf03f663f7328be01a3aa375f6adfc6e3e10f89456df78a3240903d00116a9b3572d29437bfede50d55590b4df762150fbfdd6fee2c519302c37cdbd0ca0131da0faef83a2b57f89150a76f67a22fb2825ae844a2c0e55a62c00585cf13f7c290242ac5610faa0fee3018541c5733b68415f6f999951b90921e98735f1adb294593b17e6678722213e8c446ad3a7ac022936b20a6d77b0f6176f505aabdbc699bbc6e19d082fb8318459555069abbba3d01c1b9b552da6c8174ffa28093ab52aa34c5a04e72119ca3dbeb427c31876f8a012fa6be8dbd2a81076baa410047ec186a7ca713a00714cf370d06de1847c65f81e0023042b1f491eeeff34d3b6e787870dbef2665bc02dca4ec8c0fddf496de2e31c7450d626fd10ea411776b22e3ceb7f3891f81080eabfbe32548bb88663fa270ebba72fc8c5243643188bc9ee4d07de409f501cbfa49afec7c87d1f2789d2f932f298ccc33c0fb18246887d583e8a5966746fb3bae0cb7f3053e7d807c47ee679166cfac9f11031de5328aa1e8cbbfe748655b5ac3f7b303a92328c64d738e337e2c88e4b1815aa4e0f6573d470282ce50581e75a13f328a3a0b9a1c263f5180223110576da26d8a27a0e4e674729f076e96b629ad9b2501d8f8d437d8ed00e908692403aa12b370df72df67acd5d66fbd6d6e9f3341101c4fe455c6429cbdc3d843b52ae36215a43ac8698db6dcb1ce12497b9f51d8cd0c4b78b96bd81f24d574177a98f2c19090146ab59f7d5f461a0c6bdad1e75b6da38e9147b88a655ca582581ad2b64274c7a63e1cfd278616f7d185902cc57cd18f89bbb4ce2dd17cecebad3c3ae812450131532f01a9c5c62f6bd9dbd817f5b7a07ed301ed9d92083d0724cf8a2221e263a087933e462f1975e9d2aef17772cb92841fe96566553bf90ad051da81e0bb61346c75d7de2a99a99001e56bd9dbb08dab169ec147bd7da07e01518d6951476e1758afce02df80c3fbdc515f04cb5bac48c201aa8b1fe54a520908d01d71def68a4993955aee64e9ca2d4b85bff5404f51696a7f26ca335b112900df31029b365ff086b60ab3d13c2c44465a1656c82a1472c6471bd0a018542139b4bee2741b75a8797dca4cdea4df105561471e1925e46a05a495ccb95a62bda8f2fa905bf990e5f7a30b9ec101e503b1c44ce94d6d751db5d4d01adb185e7e3de9f27268e33e71fc0136c349931194a638570f3771419d2b2dcddd74580e61cd0e1d82651a4f74f8bcb480b02daa45a841fc3f76890eff255b85a88bc204442cc35b501e65b950d5618397f528e8835d8167c888594c0b3d3d56335e4968440b7579c18a71da291ff2dd20bff984b0809d0a72ffa9aa15e477ab40e998e1845754979039b052e98b768bd8ec0df44278c207269026354eefbf6d32494347009c580731b668298f1615b5fd77cf4d43bf2f6319db631859267780752e728fb52d39eaa1f8794c0aabcd93d68844a49b146200a8dae0284da464d105fb70dff102798180231d29917b244496fab251ab80197d5b680b97fc6560a74d4627bc8702da1430dd7c2e80e72a633dc52613da653e24c43173f0374fb9ee7c31d45109a2f2dda1a6ed6e02c387274f3dd41d3d30ebbe2d0e5be93a288321e54eaafee3ae3408d2a209a285a1286870d7081d8d7987d96c0d96ca7d38d53de6db74497ebe57c97239e24d134e668da1e614fc31b92e571228eeb9eeee087b5e87183899701652d1223a0c17880be1879a41e3e4444a426ec0e9fa4af9628907a42e4a40d9aeb57244d40642f454a684f07fb45974600f06e72ba7ae4dc8ebe05a4346ff12279811e1e8ac8dabd36dc3b58fe17eb6bda441f9d75fae5a431f131ef567c49334a2026b1b06d0d09e4dc77fd44f9bba7003e5f6b6b6a78d19cd2ddea68492d8d5cf918fd982caf0cf885d3a0819ffb8da3d24352207baa1802784bde523e1d4071772552c303070de53ae5a62aae59b858aed5260f66cf269d2fbe57df7c3960c84c00ce310e336aadf9d8ac732b8504f61775296afd57ac458c4b3f587e9095473628876de23bdcea9df2fefd44148b794984b966027bb3835f4deb5d0323e3d8512839d9d7e91bbd6d0b579a8269256fadf864f388ffca58afddd505f1009a887403f654353a693b5449c09424816a4f223999c0261b85fc9e27f8044a685fd97e21c348d3131cd0ad8ab11361ad8bd542e13b95cc94bfc6f4e02864e77bc3a5ed27eadddc4e835359c11b295e862d4647e0be8151f1472a2bad617d662049c258270ba05cc3c8fee60b79dbe3cdd5892d467eb1eb919b10908c1e6bce7cd6256f08b90d498edff9b990d45e8517e68b155bde445476e86361a62bfca66c782d9e1d5020f5aac68add0d8d5e11313bd34806eb1d07998bac8aac78045bf62ef35d1e6c68699ae309a357eb91767cc97f41afc839fc0e2acf149f03f5e4768ffe12239d5ec835b63dedd8ef257ae0ecf932ea82089a9b334f50443d89da8b9d940d17cf45a436d206057e1baac60dd3d4fd5053184b5fb725563e2f5dac366e47a41671e27bfa1af5d7604965af6f65f9cbc0e187826e3034396ad39269a7828deb009d86c1876e90e2bdaf2310456e63048c0ad17f98d7e3405a57c62f0891862c22868eac6f10b58ad7476cedf652fcb48c170c458ea95e0dab1300d1477d371a0a83855b2b4be0f913d533698dc00b61e6e0d98172e11cfecd9f2ec88fe7e1282a8e3523974469c524c395b19cd1b2ae2e6a0f08b8b7e9925dd16241984f02fd028654ca0f201085d96833d6c571a08c2b680b77e6374767fd732a315e12b8c0150e7ce2da2707c402b007853edf703e5a06659abccc5c7d539a6303c10a747a1e1e6c660a3493a44ad142c672094981fe9b0699eb6202ee481875332cde412924e4d4f54c75ba290478813ee7b7f8d2cb73c42bdc9b4db044a2c033356a397d2c4c27dacccc0604af0ab1b240b25d79bd8efba3cc58a7d4e98a56ef93e694d61dd9e5f7b64e66744fba85443990e2ed06eec8d4edba99ed139bc3fc816ff1e925b004f64a5e0fad8b437791220910fb7619b648984bb76620b9995c1e0a69a700c51f7b25bbc228f229520d7655200d19e0e155637260069e8ecaadaf56dfda0b606943398c2acd8244ef5e50f797377dfc6fa9ee06f4275f03912c8b039a4e10545dafb7eb5d56f282fddbe00bfa22c42731fd13dcd08237f88c870a6b02d821c0cc4c3cbaee07da622638faeca1c035134da588eb3a47298ff1f76c5bcb1b07713872aefcf57c09befd5274046db1e94bbf346a9618cec87c5d833b3cb6bd1fb1d5fe9d37ad9b8f7d69d27e74ee6d11442b4072971d6e308c90d2f28f692c154ec89bf6cbdc150ce4340973cbcaf071d3a6b86f43c96c95bb68fe435b719e204f06a549b9859201bfb3c8f674334d15b92441bf420dad6b8ba7ed93a4b10d2446d9f477b8f4ec9d7d61dd89ffaf24851b9e1af79ef09d04fe4c7d3b8aa5fc0da2c0013fa968fc23f85734436cd57b203429df6c86e228b25d897c432a70cb14e82c91259ac6b99a472f04cb05da560a649d84b1d96e532adbd39ce1d118e024519d5d192db13745d58bd16158c16f79f890268fe33acf3640cd7fbd97228a0ad8f596ad7c30f5fd9a30c3a58d741a2d1248c01cf56fb12d646351b1d7bdec09b42a222f01fd7c10403cefcf03c0fea4708557da361d24f964088d362babea2032fff91add268b7e2260474cc0c1aae1ec3526be33d9724af9298ae7e94117125ed9034842758be7da9d9f46146f157bcdf61f8f0ce96b32970cf633a17db02a03183311f34d9192fb47068e905383e1fb9a893dbe99e7852c091eb056d2df2a36fcf2ea043a1f9f80df7d2e61ed2d3f3294a2bd6ce92f31dc6d8bbdab4f6d1c9e678355d1fcf76296effeb331d4e2fe67a71da03da01f1a18cf069553eaaa13980e46467f02c69bea54c3720548b3123a35dc4d419d66c373b9089ce74d2f1c887e07f9068b2b4151380e03ab3782c4d3477698d60306b49d96880133708412e7aa8c948838ad2e7c574a87420d2eaf8a25535161adfaf0c5dc8f246e6f831f0b0d07d5c1786ecabd3ba2c3c693d53d95d3c61376e795acc867eeedc7c22e069b5d13c0155fb52b2f949bacdfedadeebf5db8d0bf6c78a11124a650873d00014105f5c4d5e9a76723d3c67939a223b0e21dd47410856f6109731f3fd4573914528d4d0451b6e9852c06874f6a8f499644f82ff8016134533e19e7543937fa1b79e556eb2a677f43c40ec09e8c2385b5e5dda6215ab070253c5d3e53b2ff7a00a2bc71a7012e60f4ac98fe1840e7be8c09aa648b3ab0f0c27085569d6460b8028f028a0fe35cbc839b5c9e28401bda72c3972c2888d203f378d76751e957740c1177fdc8d98ddc08e28b05c069efa51c8252cbe0cf818abfcd395484981c0e2b80cc558a8a10aab8b962daf92a786433511af9f8527aa2a49c2b8bd53101d105fc145055d9ba564709104c5549bb24a3dd91a505e47959f719258a1cc6dccd06c3b905097f428b5cff87707bd0e1ce71ece41f31ad817ff1f3d04630274e7707bac1c17fe327bb81353d07980eb53d4602c580dda7f7f15e59e57cd7dbd3d90613f1fd884030e3bbfcb0d7efdc94158660e2fa5c7d49d11e08a9b723087892209124075b7d82394c4ac6a6a3e6744b706cf7f538fbe24358c280b1c6d241ec04a10e305eb956910f0d968d0f83280049572e3e7206b91ca83f558ff6a01694227174a5061617717a39664d6b54a851e1ab5c9041c80952a220f62d183fd4ff02f9d499f6808334fb92a96a4a1332be8b96b264d7ace5e3dde89aa066da81471f8dc4f4c40b4ff98e4b85557de6e994baccc86846ab05cd2e6ab2f7affcf99c008ccbcbedd42d97aa344146ad1359db6ef1c3c8e7ee17fbbadcf7d53c7b749b00d57fead071621cb4046c8d9c40b138b1e897b30d7872b654bf975575b18b540e2b702474b75054fd96c52543d85e68751471a807905b532f7b72afa62bc86c08d20514704803dc419c284bc0b953f95b2c5c957979546e79b46c2596fcd7bb07804adccc6532e423df312c056d852c6730935f95c1ff85556161503263da9e10b45be2e0cfbcc72420fec0109fef3fc1d69a1610ae256cda6fa0b1edb22e6325599a344ed9500bb3b6db80b3ed4b856d798aeb51be3bacd83e3ece79a68d750423ee12cd58ed06961b157814caebef95e7bb0384213e8f34c3f364f7e0479b248d3ce8d2005755b5832407fc03cfdfd570903d70ab82f55e01499f0e533dff1d1ce6f0c124b0fa6dd880e2c715a08bd9834659fb5dad9de7e79fed5471144c0caea76eb72fd11995bbcd26b93c3eeef7bf4613270088c9d292c5b16a2b9c5f064ed3eb9f013c40d3b53e08486724ba493c06a4d9ec7d768aacb0ae7e9168c0028372c10a7600ef9e464c2617a6185c7f9f1517cf7925c3516a0bf6a8f6e7e229a8fa104e164b8cb328d16eb11f3ea40f968302cd6aeff674f113a0d5f4eec61d57142b4efbbaf733db8415459c129ac5721a1b1bea5a025da135349fef2e86031668421ede6358b63d5078c2bedf744d0b9304603ffa2726d02e8c9bedcddb10e57654bddbf2ff23134fd892ddaff5e13c7571c673578d336aa4cd6f7ca9f30fa7ff2cf4ea874d55be1e755b749a2246ca93b7bd613e24c52ecd0e0190563713f2df9406088e6b2cd92d9b03b4e3affe99b7624321bd6b462fbdf3235bcfe52871605f0cada9534cc6bb07b31b133dea6b0f533aeb70ec48236de198c427080056e3d16a420157d335b08369f69b8897645453e8087753b82fcf8522406be11fbef23cf74e343dd70144cfa5ee7e67ab40a2dc21902315b46772eb4275bb841254da0b779691c5f56122b367f86dff07a34bdbc053a9e56dfc4afd69fa3609198d4cc852a9b2707922e89d1b91ebb02dd602dbe9cdcfc74022e67aa0eac4fd1261d7f338e3f31aa22e69b5d4217900caf7606bac392cb7b46b96b21305ed0b0781389ac7d3e23b955e9358d57eb4735c8ebaad59d1f2349dcc16bdaae8c0081d29f21df52ff42d437137ea915aa96d1e286e1a88de177775f59064fb4a84fe06aa67f3a50812921752d0fc249387a70a15aaf42811dcbbcde646c49f404e8f09121fd977cc344c9126d5a8139cc5afa4c15e555805e2edb6fe4a26460990462fabe10ea395785faa72127618e810bdabf626a3e54f78cda87a4d3c068c09e10663bc532763b0861d40cbe1eb9e8733ffc701df12c7c72a824b1c581aec615a1041c48075e3a7ea820617fc48ae9a815b01b78ca7dacab62c9f4b32028d8e0e0d15d296790ffe2507bfab1653f0e13bc3e70337762110b30e0f2e2c7a86d2ac0bdeb5e0f0ec18899aac0ae393b62e1727b189591efbaaa3853735ea9073d90115f3212a4b7d53472fccd625def7ea36ec894cb964a6dfc9a7ccb369d549c0a125b1f2f6f5fd4fa21b90dbe622eec58efbb1705e26cc444bc5aa0671d1b68d961bb5706653c96d8875eed434702544d6381308c43e8fddb0a5640340a64a287b20ee4d26b56ebf145ea85a4315980f5c1f4e64f97d3ee7269de80f1d5947c1a61fe375768d34e3151255e55d0f6857e895d118541da36e13ebdf6eee9e981dcd0d9b0fb6f9fab694ba9973b04780ae2729596e3c425dc871a404a2bc36bf5a7300e0cc098986f2091345a77f699bf2eb03d648c8f8886e8e103faef891f1f3430d57c6d441288ca100d3cb75b8e3197a571fef30525f4b542350d71170de7b63197434f4adf3c4383e89d67c298c03447e65f8b572898d86bcc8df7d5df4110815a16f3839c72f3ac89b895876947140d4aee3cf1d7ef24a44d41703097a6c912c783b7b2a2d525b86be5b993cee26e637b892bd6233a46d0412808ed53309a126051ffdd0d5e0ba154d1f3580a24c5896d172b523c26bca9f2cb842195ff17803998f55cb444e663539aebb346cb75f5dd2f096ed1989947829c17059d40d3121f726cfef977699f9f5f4c66b9073262f539766dc48a053936113d6cb284ef3182d0a64eadcc863d7267978ca1d1afd73412e3fddd3be739603e57cb3ab538b2daf94efc9e5d3473bee3b714734a85db1367c08b05bad992d964802caf16e360e0e85d8f672e27ade6dd0afcfa6eb56f6181729cbd29af3e38b3b4ecc8a4ed724b723a3ad15890a0520a9cc26af542c1092d15000e9f050f8ace54f722fca9b0fae98c4af61f623dfa3b6547dd7c7f269e4106d91c1fb7362b98c1d36f73195116ba3d41cc2bb48e150c70aeb89257d4830e1c270cb936c127a23dc0c6fc7f62f8d6e4417046d79ade659637b6ffeba9b4cff1906a2088e5f6a9609aa704f9a1165b54e18ce8e325c8671f0a81f9263ecdc7efebbf9b86dd008d5f5f7cee56e1c021e2fe60ad190defec566a5d0bab3da01faadd8362daf771f33955181342d11e1a24c26b5d76a39cc1cf7536241ecb9ac2b798ded0d845d1a27c4630316c321b3545192fe684d5ac406677e9bcb389409a0bb05d0cada8421c5344a8cdb750575e263afadfccb386f60fd58489d0bb4370f58f4dc12936a0c1f7d94e2faa22b29adb0e175cc473c2b2b52586dcd28eeb8a244cc8cb76d5f6fa9ce3842a7f61c3e506b19db42f1e4cd8a4b05faf174375a49a3345d08b0c6544bc814d411b21b8e0930d8cbcd0a0b12d146fc1706a84570251f07d63229e6b54cedfe841b371927bb245fea1098b2d4893abe28bf9cf892f53e29787044c915920868935d1005f86006eccbb4c753a380a3f46ac1467db33834c5acdff2c04978e0457583532f0610d3970d4a9b5b3838447839d1fff0506f28421b675effc4974c3bb02bd22bcd664be35d9e5cdc1ec748617d7ffce93e68f835aaec582277ee1beabf7abe2c63fc73c84ea149425c4ae255b6ef597aa1daeb19f6d5a9aec78ca0264ed51417192e91d71dd8c4b1687cff223ea19889910882de0c4f273b29045dade333512c37500afac03dadf9007c95e3ebe1eb027df638d698a535a9ad67172499858e2032af8d2c6452d9b4a0f925e285a6d03775ad1ef89ec446adfb664f078fac8222af34ebfef8c8e5c5d07332510d9be767a2a8fd3f63fc719180097b492a444a1a6f28902cbbdd05dbf2624db029395faade8c1648f360c5bcf6f00969509f4e2fd5532d79e87172cce7840ae5b302068c4e799bff095a94ee7ae5461e52bcd22078a08923c52e18c127deb635785ef81c67e7244e64c1c377396736dffed3ec1692cf5b8c4f2ea3fbe22fe38682a641486f2c458d951ea9d5fc55680cedd00803fadd8ff3aa101e11c264bdfa027ca68801f1f4c873b99f1f3b4a40abc376a800d97f88e29c845d8d9046509c88f955f9ca06da4815e00533890681dc0fe6733020b784d02b21d43155fdd5073bbcaf3e1566bcc390a9ba9b063062a48a9f3f2a5d3bf4bf23bee163709fde91c96de0c5a68fe89f2efdc3ac38fcb7c1db40c61a51a42e9804b0b1dd73cc84648a2281fb4fef0cac1feb14770e9b801ee189241e0fad6797d640cd9cf2aa1f55f4907794b6a3e4090a0074f1649e835d0f72352f14bb17efa94ebba89f2cc195d19c0a4482ec507bdd852b55c79e9d8c59b6bf0ada7730dae0434db913ece0a06828895b07f39a3e6ede7e9b2c0e1466188e1313c67ac6c4f1f57e5f5557f09fc8aa8d80995ef0505ab76c174d775f52d340d313f9b536af11d44cf8cab2f740329ac458c884671dcaadc9f91629eb57fa5e220f89ead7fa2e99f3aec56bd785433322b26e444bbf3b9ac16d3fbdbeb5caf9881cfe4342d8f74ddfc2b008e5e7f36a2441b8f69eee1c4b9b5920e6cbc1adf92f2d06bee425899d3e1103d87dcb7d890b87cab8fd75dba3d58da32f6b43f6b72a1c68b90a5619cc74439f23f6bfeda6e7a8bdcea0174649192ac8a3077f36e7212f0ef21644f05fbb965313c6b59f1a3f0e5c1eb66a2e4d73fe198551733e78562bb24ecff59b56bdfd4c04ca5ffb49bcf1d8eb8f47561c76454dd44b47618b57011a491c2c7b0c8f582b70a6dbd8e973244bfb585aeef41986969bc766b04dad2b9029f14ccddffdfc94c9c51aec7d3098e666ae5a374071a1d0ebd51e1a2b0a105b7ab3c6ad17aea375f660802b5fa962f9d7c641ade8f21d8419cfe672ca5e2881b2d7d1561136e96ca186e48709f2a388aacabe3793580b722c8d112a0bfd1d0c0af75def97d5d4c73a5cc62be153242459dfb1acf02bdebf66775fb3c52326917db73597a5134cfe5c0aff32db8eb8e8800ff2a98b25d2693445b93b94201097455751c08f78b7f961ab6bb7b72286b293c77d219585c66f12c49921c0e90e4878e1ff88252d4728c52613de28c758fa1fbd8fe1c563055120ac8565f85f0312a3c57ce1e84512a7e6f01d38bb3649eea2b549a89f4c7c9e8d048c6cc5d62fc8399a0a6f85477d1a11355dfac6630d01abdf9ae7612169430e0fa9d77c2118752bb4abc4768d0e78c8c1b258206011ff5114d300e5f9cfad20895f4ecc9a0625adf0a0ed53181adee44f47cdd416e49d035feff88a30d11e73e404098a0b2825d765e0c53f5ed8b6b0050ab489e5524616bd7067728ea8ef25bf42ac3f020e4f9b1e6deaf12a3336ece22c90458e376cc588ec99ad2985536bd510ca9f600f856a8db2170bf53339701ca57eeb9ba5bbbfcb45d3345eac67b15a9a7d845716df6841e09c71346046a67fcb5b51be9129c2d0faea4eea5f7af7fcacdddc530aef1e9d1981bd9a78af2a0d502bddd3748095d619210cf3aa8705c8492e156311474629244272eba57adaa7e846c3fd8fbbaac71b25bc3feccfa94e205e8fdb02e93809f7de44c8910070ecb3ab1dc6e088dfd54655f84063e4ad7caa07870e257f213cb9ba4e34e721d51d8bbec887896bb40502d0f4646bda6254059093df14e661cdf5d9fa1a86e2f31eeaedae100990227762b9f2d8ec60afecd697a2d02268e342042b0d69d47305455a40599e01a483736fc1d9becf554c695b07a27a27bf096c3282aad2d900c6b7a8fad55643307493f8f5bdad4c3f8d723a97c8a701fb4e51bc063eb1d8bc47ce98ad16ce5b5ffd15b5f7074ed14e9861552e994912401846c12d1ff7905485680f6a0a49cbf4e970e5e141863a92f64c2bfbfd1a1174f603d2a3e484cffbec5010c3525dc2c6c8defe0c84bf8fe3cfeaf297ea30291d98fa5c334b60f5b157e576a25f1bc5ffadd50da6da10933815c8c19605ed2cb08b7a1e4fa23741de312de8b96e8dc72c45770535a0225522978f46cf1ed21ab5f36e2809661362bd7d96bf9a687bc852ee38c59529e06742092694ee965306f7f63faed26003a1b3a0722a267a68dd9d8336bbb86153a483b6be5e6d09c82d52f1ca592a4fcfc91aa516c7b437d24fc0cc47ddd3c783900840ffb419c37d28cfc87e9bf208439bcba9dc1b3d8f3a8742bd2d40dfff4c47a402f93cb70ee62ac421452d7f50b41ecedadf39bfb01afac4290ccdb2c506570a3bb837c133ee2b7bd5cdd4f38fa55aa486fffa202bcf0bfa43707f66d8d6dce1da6e9f42f0440e81894e21855aef5b8a1099064194de5d0497407309360e77ab62298cc6a7a7171617950855fa6e0cc6d3b58d131d266a956d1487a5e73347d133f3edcd09f0298a0c490d03d5fdf00ba83fe139d3b3c0eeefa89e3be6978224b59a8a76751d077d4aadc1d2c3b7b8b03ba7c0d41e798665b99587a1349585699f793e882c17151d330f5a5bff59500819e8e6940a17986979f5806dd2f482ab65d5535c42510937a1e876afbf69f5a829709008a489b9424cc64a5056a6aa6940a88b8bfb1b0022da31d46a05fa6e2fe2f0ca37b71ef3294c374da7f869f5c5195a2047f2f52c1606a44856951fc2029a1c1144932a1a8347c3178f7353972d05d6dc04937816a787ee7a67834a972e8c73651955d9c2820d0941b2b9703b7517ab89cddbca15c281d195c64d5968e0b04f50d10f46714a669d7b5b44a498bd3444c78678d219ceda4cea398aa096457e1a8c148cf86c655ca7e1701d895f3a6c35b9e43c95305b3ef3d50f8b2ccb50a77f64ccad9105908ebb80a1b8a2f6a4cd61fe25c453133f46da139b495892c89059549bfbf6ebf55afb5a56936f9e0c4deebeabe56f1cef15bd45d2c8c4abde84c0c5335bf56f1f298106b3b43a002acf52f1bd330825014e510e11718c2925f353bcd7f7e8b2b09b57e4e14ff26b88b6fd0a94dbf308f688b7fe963cbb6669ce06450fba3bcacfe8922a30dfb5c4c43ed85062969f155d0fcf9eb57301a20fd246a091ddd91e58f93c527184325ebf94c262fa2110053485c49ae2de63239543f5660d218f531efaeb5619bacaf6ec1a531a6f3e4d2956f3fba0a2564c97acac533b19e50c7f1ee04e0c99171bb2dd2e62c3ff384f10b21e392f556dec389c7004e57f81976a8638172ccef2a2579de9a2dfefcb670f06a188216fd3507683b4225cabb350fb4debdd0955fddf0af3edb592f89d5321f89e21848a664a152a2f10cd30e5c3062a73f01fac6d93b18df54a98507d7b24e1a17e79d5cd7d7a04b6cf687f64a54f769ee6c3e63ea939943ba3ad71ba7204d2d142645247e71ba82abd6d8417e3e2af10c1744f09afbdb83348b8df388005fa197a28bfb1f8888fa91ac2f37e7c50740639428decaffd29f3d04c45736f22ba59f6cc8c9b9312bdaaf2ad0eca04c6a8ba9e666243cefb2349cc162922e90e82555416ed28ccdf080cd47acdab87e3d04fe234f5b61e56a14cfea2ac4d82142458d38c7226bb448488b603508251114422bdf608ade285070d3fb8fa19be1efadb5e98dba8fe4a0c9bdec550f90ab2bb34ed5fe5ad599d3edf27d5a35dae2aebfb3d296b07cb8f99c4a1c206b0b6b59271893eb9683fe948e6d393664831038ade85996477320edd744404e4bb9f874a5c13b49d2f690a5decca2551ace314736f8cc59be9a8275245c14730b58bfb696f7b903f798b87a17aadff25ca8217d71f6dc06f7292dc614651a3a12e26d678f12bb20f594e36d8bfd1eb61e6562221acc3c70b28ef652043eaeac624d8d1680fba70ef5f8d6e90125087ac8520075b5dec10f13ecbe75a01ced0777297615fba720f5bdf4acf77fbb61fdf6f85281b7cb7e98388ffccb8455c128f6c741d1371e4eb77ff31254000c5c7b986ec23cfda8e2c9d14b82ce9c8786199b8fd5803c96bca8cda0c50d51a31f971ad900d6fc92e57b5c54cb1628a4ad1caa3a5d00dbe25028a0dbd729902088213881229d5a5053a0191ca849866e654f9bd5ba96aca03f1f4124ce3d62b30f4e7998627f297d970ac0b1b53d1d6f6cb845e9c5ad21900150d743eeda160d5ffaed2ff2d85c43efe8d4a31c58ab21732c303232c3878801a379941be034b86223be51713a14bc8f59b8f103095c109e1ffd7adfc4e6dd8f8b6a6aaef1533c482e984b3267ab716b1185ff128ceb78a0d6c98cab05593591c276d0a646c2e4e0d1df6232b8e4a6ac844c7e6eedd02cf16525b9cf067ab4297d849911d9fb588bda630212f042499036156d5eca74d67ff132476867da3277ae180ae38530a23e2ec291a03046c5c50343b4168477e8b03995fd4b52921f85905314d69292b0dd5bacf3f1dd8a3d3211d6021a975cbe496fbbb92db2d214ccd233f12af8bfaf2593c5475177e16836f7ef420d8dde0217b20aaa70b79fa8a173f7ec8cffc3f3d741c6ec11a097061ca3fb6798ec89e5465d56f9f6c56a0f34884f5fdbf17bb467fc428482c033080d26b788806d4be674826f6cb45272eecd195719cbfd29456602e931404d8e783245ccaf9ffbc85e89bcb0a960ef799adfd5306f5a11ff98d386c580b2104a6cb3d665f49103cd770f2953ba77ab813c925e1d3d1befa01320f989200", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x0000000000000000000000000000000000000000000000088faa138749b3265e00000000000000000000000000000000000000000000000ab39d7744ee99e93800000000000000000000000000000000000000000000000b58e3fd596adfb3f900000000000000000000000000000000000000000000000000021543697d80ac00000000000000000000000000000000000000000000000303b482f3002f24d800000000000000000000000000000000000000000000000b85b3e53031c19580000000000000000000000000000000000000000000000003e4b73366064f452100000000000000000000000000000000000000000000000000005aceab8040e500000000000000000000000000000000000000000000000f5dc05a7bfc973e8e00000000000000000000000000000000000000000000000a5d05f4eb90bb6a3a0000000000000000000000000000000000000000000000060872b59764066d290000000000000000000000000000000000000000000000000001421a9133d75e00000000000000000000000000000000000000000000000f4780863c86fff52f0000000000000000000000000000000000000000000000023927881880d93150000000000000000000000000000000000000000000000000dbbcf61dcefb015b0000000000000000000000000000000000000000000000000000d0ae97d8719c1b55ed60d28990fd534c598c83f2d728a5ecfb56bd465a081d6618cec0292cbd237512e8cbd2be838836717f3a7d3b973f5b990c5a7f1ec421ace3f47a651c5d00fb5ff8417a44f464db9e3db570a7873bd28a5dc9a6bd7a85067e4836b68ce825c611f8f6ba443658991339a5094e904964bb9860defcc05dc2482f953fb6061c687a6373bdb5019e78346a3d8b4aa6358b4b56c32b61afb53bafbf7c51ff7415b3a062e79c6977d6d8564fc067ebddac69d8e8a80bc352294d758b31d13e6c168803f0edf6007b840fb864938af01a0d54ed66c99dd832480f57cbf93354d92dbe31fd406fba859e22d0b8c21ee8ad4357f8fcf75b97fad9e3616fce00d82425ab8ea7cc24b56a26eff6f6ca0f8f37eed160f45b06fb8a36fce6ad6d74bba4190b1a98f0cf95221281d936efbdbf97e1b85c57587a3c202109bd02b0ccac1c0865a52812ff25c6ced7a77eb02dc8b801760bceb81b5dd81497fa55c232c7492bc6317bcff50690bc531833b8f056eb38a2cd70a6593954e78d9f2a1cdfa67b0b906a32ff83bd1e5d632f37b869871b08381ce92e74ca749c5fddb7ae40ac0a0f51a01d2b5f7a67480c57b125fa84f186fe9f640b7461b9a0e35abb212f49cd209f27b1113fe34b0798d1c95cd6e28ebae33697debef7f295d707aefb198d9a0bc0e4b323afe706a0149918f17bc9ffefcb1523a0debc9a18cda5beba59ceca0ef0267a1b20842c607949f2257f3d70b75f00faf629ddcbdf935d0d0eda232b01ce76a7013707c66fbf745546f19d041fea89ec58d214f41dde90912477de87296d3bc7695f2a1b16e258c9525cabca394907434b9d2541d5b4ba692d0bb486213c2e5d7a4361343f0b069971fa22f76f36817ce137b60f0b5b4f0d1be40ce60e54f03a0b00effca458b4623092ca8a33bf27a0b8e9772316d687261523011c1e8187225b6ff80a03bfb97eabf1a873051ed965f503149a705b147d3007de670c317fcd6a7a516bd50c772891e2bb3315f6c2a87c3cb1119cddc826f543abc71957b0eac5663718ae339d51af5256268d9a0474a403a6ba3fc81d52d89f71bd2f831f8944aa85d6d3677e3cc4876aac91778000ccf21aa14ba977af5a4d10da02981669450d55161ca8f053a871d81b5106abdd77663f1ef78bbb0eea21e2a20aaa868967a5907ad5582638c573682a851040e436f2e6c9733ee4896a86784b2ca0f330604ba7a3c5b0b746ee42632eb4cf982d4f3c36ec1bdd34c8bdba023b0d20997d570bb040aab67abf15b560b7c04c52513aee47c6646503836ed35d6f16388eeeeeed374906d0dab5fd1dbf8e291cbec2a6927e9bc4c052c66a9870730b20ff69314b4f45b36eae7896c08e5a06e691c8449e48dedf97ddcd3d674f0b0887ebb0c7f788fa577d949c4de8991658cb0f8dc1af2af7f58aba794610ac002f87749f9d104ae27ff60d06695572ea506326c7e6bee213de54288e8bcae89c0e5751fde4c6228122ad8ff186fdf8bb495f2afbeb85a95ee5cec68db708475d117aca0d4cc27198e0fd43ab5d1f9d7ab9de2f1a36e9bf112589daff912cdc442ee6e8aec7409a15a5fe45e5c201e3a052f12e53d1a0e1ce8ad6607c91f8e9c503c1f9c286c2dde7f8d41abc15a6ebe3825163ede6981ccd6fc9b03112ec426a1fbf2b27f200f4adddcbf200880be7f5a73d27cd24d09e4c6e4d111fac582576079d882aecc30f178bcaa94f8eb567064c9ec9ecfa99034620d0406025400b7a25dc35566bb590e2b378dbaaaa71fcb2279e5b11c15b1a9c8b286b0d1e7d3d611a84b62c97b13a07825b337b9b57aaeef4f86253f4deb5674dcc7a909b0ba5a62bf59aa6a4c18e6a975776383169d14695d6b9a3d305e9a21e745b2b2839515c0daabcad6837b062b7d01dc1ba60505955ecb38d334a523d9b1dbb7ebcc565300e8ad930df265a9e0f95e724b5f713967a976ad3ff3430d23786c016722106840bc0c54b8ab48d4f17c802901bd97dceffa70035d35e38d5ff183830fe90caae070aad78800dfd19c788ef024bd49c9ea8160ac550d117d074afad668a4f6bd510ef8a176328fd8d356b31ca7ba711e3afe130b93dde15d246a37c1270b6e92202ddd3604ceeb469bf93dde8652ce468c184857e67d38139cd669a089aa6861e065c283d1803de3005a23494532da0146ed64b85e40b2de3b63ce5844b29b1d404103d4242b9372da3ab04383a671bf91156329c8325f09bca871eb9b2525bde1e641d847edfcd96cc86cdf5b50944b2faf2e9117ed17de6b5ddc68fcffd88c81a07df6735796c3d20ffd3a78ec7882bee8143ef052475edef9bed6517e3b52428923971ec747cf4f5b9b176d23a4ed0119f7a43044e08f766d367b5cd9ef5610ab73ad97dc6bf4d90f55efa1219facdcc830df23431f5629b286602d4ae5ef903199317dd1e0549502cb2eae16f20916a284e792e268e3b7a57b556ea3a42e9267e8565bbaf76819760ea487f2b6442945c0343be29ae94406f3bfc0df8fddc08ac82ba23e9335dc9f9747a73ad89f80083c64e4e0ca3c3ec5bac16c5c4b98829528096d11677e5b2add86d8d3cb2ab4820a27e6b200c99cb89696853ef6f65194da3ed8a69e1e02e7c956f04f25d64f6d43196af74edbcb57c753c75bad26b046355828fe4b56d8e7b094de4c5284acfb686fca6769822013917b3bf609de92f0cd0835181c4e1e9c37cde2a31e9b44c31b5b6404a6c21cb03c5e2ba6abe4e240fb2bfd29aee712736279e486f8a437aff7e469e85bd5897caad3b28a75e0f2577163a579747d38a674aee66657777f9eae6deb89fa31e82ff1f2694aa389b0d32280e5e10ca4652b0592d2f88f3f966aff1518c47d4f7c9de34cd5193199900117b78694a8c54fce6ee189a22d323e097b90b66f4e2f8012977f5ca861f02178255d84c899b963c3307fb1e41bf1f9200de68ac04bd3042eef9907817e6771daf9d3a34117c81a9c27021cfc836938d33259c9d272ad7d1e7a273b7b54c0a152b720fa2026885f1f12bf2656c147cd9c54fe4b76f6cda531400c8178ca9f2013c91ebee44600dfa4fafb44468f6b425e50c634a024094f8cca64359515839300d925b49ff62a8510bdfed6de3f229654068ce3d956f0f4773106f38d8d40908648c87ccd1c64a2d1e621164c253ff9472ccd8c804a718b313952b120e2488267507664201bc55514d572a0c99e79068801972bc67b062ffaf6db639d590000bc5b218af736380dfb85df08d376733e5f09f473bb3cf82902a0b1070756502076279e5874329df3255ecafb485a0df06e08147fe1f3f85ba6070df13f7552e109ce9a119487805b843c229b96ec320193763785454fef9c984ead471d8a0400a44f3eff7fced4690b673718372c28a8b6d6924d2d0ffcb7cd85d77ae1d7e542da333e9ebc4a6d3d8e4a18172c4a282c170024b8be7a889a57fcc75c1486d7d1b942c027af9c7f98beec95bae62e76068b236387c0f10d19c4afee1274960cc29b78f66787e9624cc182ffe8553674830347e79f7beb68a8b33debca0791f242dd0ad1bdea9a27eb052b4a873a88e3bd0f3be38d6447938527ae035114eec551dda82ab01c2c8a87c78f8441448d17832161b30824d95a71b06fd78b051c04f1be9ad79e03364982723397fc744222f42a3f9e50a53f14d94cfe4decd554040033d5be4a7665044745238a59ac3bf71264854501f0ef4ee5f670690d814881c259967a6df24dc395e0ec1c7b9df807e2280a57705836b38ddc025f3ce8ce6b22c59fbff66af05881bada0c7b1db9a5ba9b601b487b49a9fa48678e51b413b471c1e72a1591e1277aeddbc21c9e887e8c96997880d93b2865a610c04f470dd771e8dd57f4d99b31007bde7601a1873837f2a88c0dd1b886c1ed9b7b411393e1800753e85451a0a3669c9f9a909035e6455bcf5d67a1bdfc4aa243a17c16d6ee6100c493ceea0130062e564d26ad2584491a0a7e215674f3ce0aa5b68f3e3f80f02fc04703bd2c4ef3f812bf23717525e2dca862c640765dd9cbb0a897166ede12c52f69ad514c2b963166d8a0c16223a50170f085c3cb1213897891a2a4d0d6f19ba1a0b8e7faded21a7be15188a512a1e6dd25c9bcc6e6a31ef31cad5c0cb8e1932d820675400dd30942d518df2005ae322f8f3190392739e0b2827b725ec9c2d6738d204989cd547ba1cb3bd37ababc597f124283ddc47a82fe8a4fa89c181082cbc4ac86b67bf74625314994d870426a846c6024026b841efb1b1758390e713f1ca562c70d4385dc5f424727419fbfa3c6632ce96cddef10b998450a8c6f601c4418da4d9e1e56c4eb86c8fff01aec4955f1ddd3fad61f249b6e1a74e0520226c7f49df1282612cfbaf389d6f9081269abad88c9915f8329c6f7f10cd30fe22326f40b9c864dff34ed6c32d8934e1d7a4f42a2cfefb8680b1ec3ce5c91aa90004db4643e5d9b5333344b28816904858056b89e854b880fe2ab9c421bbe47d2c8583c3567a93087fb8a72181db348cfc076505940124d9c48723976a84af7a24568430053eee947b3d1d420d1d3f63ff8e9d42e558ced36e2e5d4c70c47d9b28528cb14379044613b775464e86f94f62e29058c9869322648535ee9bcf8f0b12b1ea3acc204f519f48096e19da14a408266e0c75a2f25a47aff402f8205a562df0af8e6b8ccedb6f16128fb2d577d884101b679c954fa42f4ac6715205efac01ae7bd1f024c6132107293a1553365471c18c03ed9bbb3efaba7a9cdcf4afd7116da94d4d486381dae6a51c4554f36a25179d759f998fe4827fca97fdafcccb17f7c3b9b29db30c16573e4df5197879a1251d3a20015accb4d22271892b67442848049adca18dcbabff5331e409d702b9f4ce5dc8fc6efd52d0205a4516d9b40c64c134bcc23d7627d66edf1d4cd52ae985c7acc213a6686aa7fb98a4cdf9cf2d51b9e05b1ddd31dfc6697cc75ccd917f08a92c8ca8c5faf475f0a188ede9370adb00419489e89e97aba84aa6c77d951f624f9aa99955d56625553c3e7a14ac2f1a9afa3f34f815507ead2a646c9dd5a23fb9fad905ed087cd01ecafe4c903c22aff103f2a3ca614f98f8542b65547bf55a39abd637ebde2b6e437a98f54fb517ce908abac19a16771f8e4ba0887e1b3597b168235be0d6c60d3317f9f792e60863b4060fca1d569252f592e4be383d4a6e86310499886aba36263aaa661f701666c210f6b18ba1d8923a713b639129aa452867e0a8cdc1ae0018714ac7e5890ce6651f7545b2d02d5786a59352cf669926d36a6e3f754a84edfd343d7f88dd270c86f6a7c997e90366731500e3fb282d8c87e1c14443fe2ec4443c7efcc51f1a7f9bf1ffa1be301c4fc73d6bbf6a01b5340fa3d004fde4da36228cc8018b8e27eacdb17bbdd7264a4afc92d63c6bfe9efde14b06ea85a69d6b3100ad8021fe0775f8e3026bdc4607fb045cb447f79eac7d124c3c42a094f9f606e54b2d35f52f1c9001bb4bd8ababc8b16501d01ad354ecc264cab305e988705243fb7966552bbdaf95b7047e6266edc6b12e49e88ef2ff364122afeae596f9ce91bd35073214211c436997a4a33d6fef5e70337e672d8381a93f5b4ea1d06c51c21cf2096e1c41634d00c90b794087fb215c836e4a64055cbb9d50de11ecfd6a1c3060663b2b2e216d082dce2579c3994ead2afe49907335ddc49d4fa813729246dce975da1681aa5fde9249e2c1c140de81649f13dbb6e19ae3a9276ea9d0a08e1750309b0b5fb997116c1e20f25234b03c264b71c4a071bbe93b6d0a69e96b3b8edcea6018758a2c7c350a89e5428f07ef709b5bbd06df80ae113dbde9e61c9dbc99f9022595848e4f0adc98fb3c5d9e954a6ebb28eea6929974874e43277ed5837eac650cb979f976b878063d0221e6cb4822bbe42daedf1076495c945aa7144ab839c9010f5803e219631b6d3ec25d9b21c36d1c2a68efb4d8988bc82e44666690f1e8141f3fe11884f2331e475ba32ab2828b967bb83b575028e4d557953ee3b31ddc20cfccfd83d09af4f819ec755b026d64700d8fbff93d76356cf1aceaca758b3c0938ec673153913cd1287aa0ddb0f8c55cebcb8b6bd95e693e32697fd45f0d7f1d346b773dbfa16ba8917577a6db93867c5dfe647fc231a750f07426e89c4f3d200eaf6e0111401ed6742c6aad55fa225545fd321c94f5edc7feb241fe50fae82fb8cc5f9868d4c5c036e2cda9da4e93057ad89a8b90102b0c9ea0ac2eb9d5501113836c665cca9810f9dca35f399ab192dcbb0e7519c8e948014873cf2ca61705d8ff4fe49045287dbabf5332675a57e14ba2810f3e537644190b9fe8dd2d3120d5b7f4fa873c1b794cf3eb623d39e3a81d8f20115bc06dc33bb3eb7e353f242815891e9dcc9c791ffc59e065f8644e2de3f017d563a9b890905bebe871ff65059ecbf4d0d89724072e1f589411113ccc8d23ca44a1fb4025fc2e668a46b8ee14499b147e6396c868861d8005d999377cbda38057848aff4f8cfc8e8dede9dc28fb01a73e4dfde3e362611ddaec86726e945e330c5c20a151ea891fe74467b60d80010d0eb3028bfeee573db93dc2df313c89c1e545cee7af941eda5eb8d76b0d7aa7561c776b8398ea77ea3611f65571284075419c9775b05ae1c25cc9c5270c3f7757b225fbd1bbe5a0d8a96f0a5712ddbf0e5b143e1d4792317599f42bad0316fd73d3a81d365df0237c745529a2dcedc0d7b8f02501807d36936d8a54d42f1757d6ddfd411af39155b234e2ca522cd89a0759d0c9b3009c21e43401e27e22b3bcdfac5a4bf8ee81b1a1089eb4a643126ba01e0a5ee69879e50293f1a85317111cc292cbbaec3354d0863b0707b0cf8da5ca077515b36f5f38e87e4fd70f0c4ab1f4726a7cbe45ef9f9c657aabba65cf75f300f8b159ba9445537e997d1c0a38797ccf19233d14cc678b564aaa62ba7d6fe01172087176fad6c022a03b790b771c9fdabe10966cab2c1f84d6306d5cf48bb0347d80648abb1cbe78018d9c23d23b20d882438917282c6f58e00ad0bdeeddcaa14e7d16afb4a43c54369156120df55e8d76925bc74e69051b0921dc7b0d875c31f39c25c4810020ee8e84c415772d10884a64cd58b1124838dc77ea531ff6a4713ff7df0a33ca14a524a1f526e18a39af4a43224fc7a5963aa752dfa3bc5404e3618c253c596d477babc8da2ba0faa41b3133f67892399ee860d6d5e8d9ee061e39abade8d637732d85f21517083241064b003dbc1841770c5d0bcd04fb2503548694c9d97633514ea2edf6105ca39c7d4b0f3d36fd793044be89b2fa706d58efa0bcb573cf9abe4d11f83f0c183013b5811ae5062ca3bc440b364998342c465f1668d4bbfaf12aa6f410392980d6dd86acf51dea00cba982be6df81d2b8b4cdadd98c3bfb9a91a771744250e604c7d24c359ab15538ae8245cda8ca13e343beb9a85573cd96828039985b30d246e7a5d651c10d38d4347fd971cd456cbc9e78092ebafe7df820a530f3a5c287b615013d1c779c150c58c5e31c09fac48cc02f8efab3734f8a832af325784074698dd42f5b194bd08879c34a6df579956489206b6164519d972288334d47611ea33bb49bc107f82c2345721e796f87f53d326e1ac09c4f18fcab1713ab76d0c7144690b06cb2dc939f5211d1d37a8ceca6f6525adf710fe16c845a4fc7257281b842c19810912bbb1181a95664dcf9cdba87933d18a5048fcfae7664ac7931fb028726b00de1f6167b66edf4b3f8c745d12ab05fdaba8ca21ca7af9da2ec00659d049dde97aa49977a67c603f4c2bab7438f0bae36297c1d9aa9e896ccb361a60e0636664c2e48de1a528a045b3a01560b636d654a39e0dbe256b47723048261f4a59ce977e1e501672a0cec140c227e4aa4ede24653dcc45c5e388676a36072c7fb30172fe6f20ba8947997fac2c3696b3ba00a0d5a72ffcd5303b757baa093f434459a7ba88638f93c652f21efecf51ac4bd77fc06754036ced23b37fed22cef64f7cd5bfd1e0dce9c8a5da6d2974277eeb9c58a9dc3b3c6377f306114421d1197fc743dc2702a514ef2005ed4b34827ca67956924d3dcc86b7d9b13d340a2d78116c10b1bf75ddd470444bb558272220216fff391bf80e073927da0f0528c0154b1ead201809b2fbb9e90d21130e0de106672c7ac9b9258ee31b0ed20e1d2a3ad2f14524dc6dbd6519ad7669c43718baeb840b4df0d60b63ef29e6e9dd2a2592a2485b3d08d02c3c8404e44934216e34044a22759b8b3728f223218b5f295780f9cade7602d5ee2f0e082b8496b6ce04ba731f12b75cd0bdf676cf2af817639ccc9cd465e586fafb46aaa741e92e1261e6b717887d3c59b46614c6755012e169cc4747a8ad9b7362db7c3b79961d0866bfbf14d6d6183088e414c4b3f81761d6c503bb919c930b6fd5183919068902f359610f08722729e69e6688f3710f9782a1fcb7c9761bcabdd9797daab86a468f69163c49e59d931f3325cf2313194ef9805a4822a4426e999d895f9be09611877935c4de99732a2e1b995a04bf293c16f50dbe83edf3b65eb4f1ef97f388090f68a459cc0418b59f4aaade9b54115cd4c9cf9c7cb92fe3eaa5773905f71f9d1be656454b9291abb50c854777260b0765017ff0a27eb26860ece0b01d7c55ba4a8c228a82c9c5ca23e13926c5cc1a833344934d57ffd077d9071e17feea84956531aec7e05bb1dab9756ae3e54b2cac7af90c75f6375648c2f8b674bbd9b185e60de61a8a394b2bba945c779e890ca1867869363f0464d34287fa19894f703d397a8d6a458aeca9d7b4e58183d80a8b15dbb1a653c8682eb2f4b5b15a2b902e4a5dd9a7dd0781e66f354b81a10c2c6afc3f1ee3f06e4cd03706ec2acba94d3cb3bf6f922902a175e7e66c7e72d02e8826662e5e9db072f79431dd1512a7a736fa613a54bc5d0f48301bbe05138a07f996452e14dbba87e625c5c707453408d57a90b5e1c00b32d529124643903a1d630414afe6cbb4844f94564047eb9aa90e20e6e6e8e613dde4522a110711ef246c165a05721424b72ad332073bea9cd9fecd0c17399707dcafe6ed9bdc294a037e9338dc66c09b9bbe36985c5b021b32332dd917851e19901e49fe96834d851f697162287487b87c4551915ab4b151622c982acd7b0b9929d96c94229bb39b294f5b3cfb484e00e734c1f1cf554245e8ed206230380af73e79c4db31bfac732de6f61ef04cb748a47dbf487ed550657ff86e49bee29a65b1715a2a7e5d3ab32e1b1433ad80a8dea89f7575572699057956128d7e99718f00c6fa760e017f0d2ab1d11445f18995ddee563f4be7edbc55c00ff911054d40b1a55aadac71d64609546a0bf7e60d23278f4f271af9f104608d828ddf9eb57be964fc18f864767121d36926304da1c1a148a52ed35f5e1a3f6a51829d2e9229d46397235df4a5040cc43d240a26cc55434b407257e196ada5d493a715143f5cdea8530e1d45d1e92c391930f76ad6d19ec4ffe89a0bc977aa9ab20c15ecc1f20258e01a98876cf12347696e548b31dee516049b98cde66dfe19251ba1ff94020dabe324b200f0a7050a238845983e210c9e15e8f3ec7bf2e6f18725e4473eb3dbe7c9d3672183260b6a78bfc93809d22bef74d3565c69fd13b8681d6a11c06d73dff9664c1ebb58254edd9a42b57c50d0e2f7b95fc10c669079664ebd96a6f6b7f4d002a77ed57c04d5df10845ec831a420a1c982975d1a6429b32f76e6317ec8786511386b681e25cb4398b7c549a0c0d9a732eeb5c6764e4f87ac90416fcf22b3af1440b73eeb0bda7a31c39cfd50515ca746a465382099ad154f57486ff330476565c51fdaec116cd06f12135c0e45eb7e09b570a61f018e005b23cf44e6ff57866c9c8b41fc0345299c562c4736f442ae214374c27d57b305127384f6246e3e77da25cc699f1154465412f4241252c9de6a83d1d3d3dcccf0698509119b402e4ebc99549d431000adf755ab7bc2e91eab518f9a869cf90437100a41efea719c022f1112b0732d92aa49a2e0b61579304f87fd5f2efc52e7c44e7833d768b2235c7a2d8f9d2b29f45ae0e6a7e4310d26798a38a7eb97e32fc9dbcfba1f7e21bf9558f9710ac719295105d1f67e56210f5b92470665106668ef157bdec212cae3e9d3ff5db6380510092ba62e722d8e15b63dfc208f0da6da7b7b8c08f1fdb332457579997dbe1a55656fb14af1665618bd99835f2cebd09fa5fc1e2c6e7b1623306fa3cef8222118a484d042c7303a487143b51e3f0362149b02d18386da67b3f2408a8d54600334f79c6ddff8fe36174a01050d23ab2d4824a4a9d2a83a72e9d7a5913341ef209237b71535b7642d53bd9221acf76ea745cbb20214b6329b07d4ac15e330af12ca05b10c4ee3534cd732724babb196b5fb9d10c6dc91e1ca941bce024206b625cf7d37d944300b328be339a3e68f309e9b49208e5b128530b10c57804c7dbb2f53769ebf98ffa07ec1792feca939be56afa2d3c507f3548896b5c544a36b3f001543e006128d8660f2c0ddbc1449c8abb1df3b43d20f1a5025b9d702735f76085ed8438f7206db012caeaed7393e2f38baca305a21adf1627f34baf1c30f080b6dee62259851650721e019d7343938a92790bdd6254529c0f808458a9faeb22fd7647ee7f88ac42223461145a13beacf792f2c1732413522a19cd8912235b20392ab99fdfcbff0730a4f09266760360248046179586ffc6aac16fee4959ed42fcb2a42eae6708f5321b81cd2a6eba65b4e4bda95d492d29eb4cc91a973b94604aaeef27e8d4380fa4348bfad2a5d04ff0c8741a4c6580e70b68e2ba9e109be048200f2f679401340331225721cb55ae25bc76cee9a2e91ec0fbdc7c52121d619e6e8b6cd9b22dfd385235c5fa74641630665df948bbd625b2392b6ddcae6510e57f9b5571e434d164d58add1191e991fefa22ef88ddb9f5489bbd2384d72ca237a23f5763adca7ab06789342a05c3a6ab31726d9af0bfe9fdcd54c3018e8292f74f83314abc69e0c017972849c43a7befc13a798510bcd209b693392e6c55a28ee660e838515626c4e2508b5f957fe493efeabd2fb47a5d78d933e77f1d36a22d4c6b8aab3c7fb36e74d09e8ba6c523b1d700b66ce264c5b282967c63865e52320f520400b50d0ef2a08222620180d77baee87a88f3277f9f07521a15526a40ffe656543e75472c2ed85b40079ea7f9f5a256305c80af89e1bb4f01c8268d02f7ef74c73700ed2635aad1020b702196334d729bdd030e68a4d8f5a34c32ddd246078d8a5730262a24d821ee291398b6acdadee7435f04c5df0c63e76cae163194fce3f1bc643d6b2fb4343c5690da8dd0b5ed100622e81565ecdc4c087dbce2b40d61120db9c0a71d36d0b2fc435f1da021f09df04f8d36d769bb321ed0cf31e43aed2037954298ef427efa3510b0c3c206caf6547ebe2df41480c737356b706e24fcfa855fa8271a3d0c199b3ce7537b1dc19b9e187bf9751bd1d451ef42c088940be2dc8ac5d8593360a22dc754eb06ebf3c18c15976f7a370bd14131f01127253cc2a46aac7bf0060a1ec47d431a6328fa2954d56da83cee9f06e4668ec07be8bb72ad9814f1be714d239172334b189076d76c73a58eaa5184b686ebe8101a8c697fcf172d2fc00e5649329913813eabae5ecefa0adb5a8dc104ab1618c050924147e8813b32d6b2801294e148358c514b2b833f5e3967fc39e887532c43015a15850353650f0a78495124ffaec054c8adc76dd625e75c044f71200e0d713017ad21288895f478722e00ac38d4996d0a8f7fe171a795d19746ec017ebe31650700f4c76eedf63c9da0a8dfdd4b27fa8a699c93d2a448f7d06d9dc52c4930d050ecffce1b87fe2223283f247434ba2ebf193c7ed32333df5fadcde96619c2039c8f6e34b099aca91b8fa88c3f290d6dd09f106142bedf0ac29c1f8c056d22a6ce85f45df8c5ad17640a9f790bdf9bdd55324c366fd72f6d75ba9bfb0d1be288316eda51c89921428ce04183c87bb0a9244fa80f44f03b973351ad38ab3bd24e709d2c7de57471d47ac0df964339fa5a72efc3da90692468046f3f24ea0f705e2c59642e41084ce3b47ee6790d2b6bd4375231bcf180a0fcae273dfa23ade15de9325fa0b39a74136e0cffba7d78cb5e0bbf463b8e089e5e62de7f99e0dd31291daf49a2a338755fc5f0a1ce1c3a9f1002bae3b98cd20aeb5a8de38b42f412e05e532caec3a035c15f9ea3f8db06561ec62ce0c7c0c12422dea24b92134820cfa206a98dd13a1b0fcb0cea8e22e20a3f3c33ac959d9df686c6b551bb4b10211e79312775da1da6a4b2f100bd9ab53fbefaf3ae09315e7a73f7e1ae3e0090b0fa21c0e42a1df058480618724085e343a58da5e825852cabc240c940f14fddc0e99cac75b8d9a5c6758b55d6e4f9bac10532ebc34d243c09d684e8277b99cb32c1a2d59cdfcca7fcee82b75433c2bf0df101951bb97cb6d631528d1497d3777029f3dc8926abb3302b92c0a8f04eb79b16f945dd114b85fd7a944bab027454b1040c01850b5af972a3a996295ef6d617d4fba03a75390f548f465adca35d0362ad013e1cc6760e9d018c26eb2b9f9c0d6a8bb18bcff4407ebe1b1835b17fc822048485770506df4d49cf421671a025f2e3db015310776b35a82fd2f586b21bf2860ed1373a2eb05749addd1898d4bcde45aaa34d4c94dc73a44a9f5266b9e311864709503a3e58518ce96a2c3e8dfc1e447609ee2cf76b6ca203a495c2b0125151c01ce091cd9e331aea74be7bad8fe0138c94b033e12da7c2c19cc8ec55c99091fb9d4006bf121c31dede198a0fdb9aa08420fb9f46eac4c803db032ec7c7318d16a34197350ede6c0343e321d3e860afc6fcd66ced27d0c13142518f054761d884aed3762b43ecd50a59df25c236e708789b8378409951cf05a14ae2f8c301e86e10e9a2c75dac1a808064a70a783f1bd6bcd5d1762e588332d3db71ad77d2c66895f66e4678a867b72d8fa053c51ebea627870628968d693351d2afffd301784ef0093bad30c1ba4ffdbf325c685fd021d468b962ef6e3834b1b16fc1a8026866eb630df467b433bc5030d03e31d1845fab0e2aad4e93678cae3b237e8751e0f33f6eeb7edfeecb02303f28ce4cb8fac0746f818f7b97f80284d2981894116758388812c470d1c522502beb11a2baa91bd7b9259977e6bb71cf538d0ba1f080cb65252c017f7d915cf72e1305cd9768c9cbc233d7a2cbac65601dd702b9b13f84d42fad54b4918a79b6f9b87317a1c84d61bdc347d2b2a707baa44e4727e00fd30dc95e3ffb7627e96bc745a782d686a6590d708e6b424ad1c38d32d78030685bf5d3407264c14f0f86a8db1bfd48a0ef5cc30565ec4af80f653686ebae92ccdc6a6e56fb0ecb116fe6bdb5e9b5df8def29e4d13ba2b4e1c43df8ac1ea591f708744d587ec77aa0f74434a6c0775234b42d67a303f914b2488df06d3adfb21dde903a21536b62058f8fa4e09577da8dced3d6a86f8c9493b47afb9e521bc2e0377854af2ae69bb1af04d10a8faabdeb54259dcbda8e554725b9f33a07c432a3e24bb51b780207643253c9de986abfbae1d89d751584a90996100e543d1482ce8cf358632f600945281f97aa741f687462fa3a23c1c01cc686030b029ce280ca0d82cea2b1c47193e344a6e4da2f3a256652bc22ed410f68ce3dff1894f7a1ffdbef96a47fa72bd8f73a38a915c2b099e0b4ece88a14bc60433cb8e405b050b234ee3a351f3635b76310c77f8886d3281c1b7a50f3626a2cc0ea25994d76d00d28261d65c0843cdd25b5f51f0f4e5c522c4aa467a5776b22d942e3e1de4601229057f6cbc81db25945b80c9c7762a9417c5e27965c09a3721087a8831cbe90fb7d1586f76ea5e296da6406828b0fb40b3322182ac14691b31ec674b650f030ed585df07aca077101dbf75aeb7d8832888831d9362571d9ddd452025455cf92707127b8374b4f589739517b47f44ed47c06f09bfa08e7d71e37cbbb95c9648300cbe5dc0454b493fc02b0e2babf80ac9ae3db7494f0cf7dc17e1e8ad12586001f75c172eac0f81ab8c4c2a69694dc9ca9442ee08444baf13bc42d3a8a9f1ed258b34cec704010c9cf981c7788d281f40d8992b1a2fd99509346da4e18068df280916a9553dd08cf6afac441b574aa35b74fc70521feb1044e4ff0651536c0f081ef95bf341fff600916e297ca6f383dbab078e848ac0dfdcf5a8857477995105d766a9d21236c4b06ba90d461862696361346329ca90b3a5e4fb045e6e0853277376295c715272fe5a774c7282a90b75068993786cd6fb6f11f671f10502ad01876b03a95d256fbe9d0b2ca9458e061b115c572c4beb67cf15bd0db54a1b9f2998435d14c36ed021240accc567f42705d1d96841da9e4aa166b30be259771a1eaea35bdac859a94e677251349fae7455a5df632c235b0d25e53e019da6ae7100171dab09430d4db4986c2716a8626194be4b67df0d4070e6a007acde0809f9012832959dad3f231e74c2dd4d3197830da3a0e2c48073e5cca2d41b5af904dd19078c02e4f2b4a2d0ab7f080b92fb061fd1cda443d33e9761d04b49b976141015c3d2e7c4e94871a724b8f24df4d4ab2688b3a0615bce95bcc6e919b1919ff3", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba010000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } }, diff --git a/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json index 3d536b39c..68dd92015 100644 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ b/circuits/benchmarks/results_insecure/integration_summary.json @@ -8,114 +8,114 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.111521499, + "avg_seconds": 0.113595944, "runs": 3, - "total_seconds": 0.334564499 + "total_seconds": 0.340787834 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.610321888, + "avg_seconds": 0.606440319, "runs": 3, - "total_seconds": 1.830965666 + "total_seconds": 1.819320959 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.559050209, + "avg_seconds": 0.558063459, "runs": 1, - "total_seconds": 0.559050209 + "total_seconds": 0.558063459 }, { "name": "GenEsiSss", - "avg_seconds": 0.124032833, + "avg_seconds": 0.124646278, "runs": 3, - "total_seconds": 0.372098501 + "total_seconds": 0.373938834 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.226692527, + "avg_seconds": 0.227111444, "runs": 3, - "total_seconds": 0.680077583 + "total_seconds": 0.681334333 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.500284375, + "avg_seconds": 8.433242125, "runs": 1, - "total_seconds": 8.500284375 + "total_seconds": 8.433242125 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 49.366586083, + "avg_seconds": 49.593938792, "runs": 1, - "total_seconds": 49.366586083 + "total_seconds": 49.593938792 }, { "name": "ZkDkgAggregation", - "avg_seconds": 21.116986167, + "avg_seconds": 20.539657042, "runs": 1, - "total_seconds": 21.116986167 + "total_seconds": 20.539657042 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 1.465883083, + "avg_seconds": 1.48974143, "runs": 6, - "total_seconds": 8.795298501 + "total_seconds": 8.938448582 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 62.328322972, + "avg_seconds": 62.378365152, "runs": 3, - "total_seconds": 186.984968916 + "total_seconds": 187.135095458 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.200691667, + "avg_seconds": 2.134972792, "runs": 1, - "total_seconds": 2.200691667 + "total_seconds": 2.134972792 }, { "name": "ZkPkBfv", - "avg_seconds": 0.336088264, + "avg_seconds": 0.340530111, "runs": 3, - "total_seconds": 1.008264792 + "total_seconds": 1.021590333 }, { "name": "ZkPkGeneration", - "avg_seconds": 1.351367042, + "avg_seconds": 1.378689777, "runs": 3, - "total_seconds": 4.054101126 + "total_seconds": 4.136069333 }, { "name": "ZkShareComputation", - "avg_seconds": 2.682164854, + "avg_seconds": 2.756735736, "runs": 6, - "total_seconds": 16.092989126 + "total_seconds": 16.540414418 }, { "name": "ZkShareEncryption", - "avg_seconds": 2.506225536, + "avg_seconds": 2.547185821, "runs": 24, - "total_seconds": 60.149412873 + "total_seconds": 61.132459711 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.176445291, + "avg_seconds": 6.14141, "runs": 3, - "total_seconds": 18.529335875 + "total_seconds": 18.42423 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.100550749, + "avg_seconds": 0.097622333, "runs": 3, - "total_seconds": 0.301652249 + "total_seconds": 0.292867001 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.221828033, + "avg_seconds": 0.228617342, "runs": 5, - "total_seconds": 1.109140168 + "total_seconds": 1.14308671 } ], - "operation_timings_total_seconds": 381.986468376, + "operation_timings_total_seconds": 383.239517716, "timings_seconds": [ { "label": "Starting trbfv actor test", @@ -123,49 +123,49 @@ }, { "label": "Setup completed", - "seconds": 3.0715085 + "seconds": 3.049949667 }, { "label": "Committee Setup Completed", - "seconds": 20.22829075 + "seconds": 20.248450958 }, { "label": "Committee Finalization Complete", - "seconds": 0.006707541 + "seconds": 0.005924208 }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 304.143739958 + "seconds": 305.202475458 }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 306.698077958 + "seconds": 307.797721583 }, { "label": "Application CT Gen", - "seconds": 0.313315125 + "seconds": 0.3082625 }, { "label": "Running FHE Application", - "seconds": 0.003688125 + "seconds": 0.003257541 }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 79.884513625 + "seconds": 79.923254834 }, { "label": "Entire Test", - "seconds": 410.2113715 + "seconds": 411.343670791 } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x000000000000000000000000000000000000000000000003f7062d91e599a1d9000000000000000000000000000000000000000000000008cc7102c928c65c450000000000000000000000000000000000000000000000075ba3dc02d69ce9910000000000000000000000000000000000000000000000000000ed71a67536b000000000000000000000000000000000000000000000000e44e8edaa774d8d700000000000000000000000000000000000000000000000069df7c477ac356a5a00000000000000000000000000000000000000000000000546c35bc9a1e69c5500000000000000000000000000000000000000000000000000021ee487cecdf100000000000000000000000000000000000000000000000ab881c361ac7b250800000000000000000000000000000000000000000000000615484820700c856a00000000000000000000000000000000000000000000000bb5ddeeb0299d72f60000000000000000000000000000000000000000000000000000614a8dc34464000000000000000000000000000000000000000000000008f86b47c227146d86000000000000000000000000000000000000000000000005b0124c6d784e70a800000000000000000000000000000000000000000000000c7ce22f6009a0150300000000000000000000000000000000000000000000000000028c352ad51c1f0a1e934d28e26ddbc2239f70f1f2a218f011eacde911ac55ca4f56e4c92826ba0151ec8baa1515407b51df92e0368977054f4ff1d48781d8c1bc7962286ed7d20c520eaffc4f8e6351751bd8fe1e162471d0fe3163d177d0ead3ac22d2519a17089d86113a4a8393c93b80f3bb7c9dd5bab24c560c9f0d8906322ab4d14bdf2527575e8ae48fb40af0adc9c7df54fcb65138a862afbaf04d592215d462bcd29107c4ee919449aaed15296c3db7bc5d246d1753696a3fb0d71ae9e02085318c402afd41a4633e857acd0967c13bf26c50c6457ab5dd898ac1864799b9730689d52ad93c530511536a3714b5748aa649245ab18e286181818ec79f628930d4b39d1af275a9bbc79f11513fc819f8a77429a99f5c19dbc3aaa699028fd84578ae771d2cb42f9080e97577d5433096dc7b5872c8a57058cf2b02f8c0e531169ac86d010bdbafbdca84bfb0e04bad65fe3350d55fbfd17c3806f415f2fef12c85a5cb05693555777afb1ed8c9e98ab2221191471b57f70880883d7f77cf7774d4fa8726d1e7542a8d79306c76a221eaf68d96dcc25f7bfa67547c7e7e6823a8f5036a0520516bf26d60b94a8dc1de281b183f97c760db8ac7dd87c986a7ab87c57a3521148a502af09952b50e41ce4798bccddf61aefc5c08cbe35204f43d0c36954a0e89229a121eb3d18ac0c9561aed66af56e683b1ba590eaa4edc1d6476c2762208096dced400b6f35fcbf000acf60ad54d9076eec61d80121dfff0afe26e749c2c7dc41f5ec8f3774ba1084aa7c4d494798c31c4b33f0371083a13e67aaaa9fb297e0ddb44886687d5d35b7485cb47105303ab9d1a237f8dd9dcb93298077c2c1bf65619bd87d561ea96e41c5106896c2a1307eae3afc8ce2c501e6ae5183d9b302f94e692484cba7c25419a42dac6badcb139955e149eccdee3c1b2bbb39bd92006d24ae9e0b0607f5c1994f5c0eec60fdd95dcd71c256fde7e12934ae95ff62c1c3afe38e4250234c03b3fbe574ebdc4b6acce335cffc79409260039c2c5890f33e7fc5c1904725888d81fbb00dfe5829b22b86c60cc60282639614406840619a2a11d45659615c74b56c03d94902360a16b8813b318f90cfdf6d941bd27c11c2c40020df0c8df1b5f56cf8faf6e3f78bdd34f134ab0857d93cc7fc0d8d26a084aeb7c91a1d79ae529ded891a7a2a94b774927a6747644bc87afa45c115cef199c133466b246d8a8f6a87f50454203689ffe4a63c722e36cd714d94d5d8ec715203e52c6915f66cbd5ef50437c703b5c3393cc645517d6c2a210f9ac23d88a2d480d46aa3febce868435cb4052c6af52d836aa60a7141cd972100fdea65c6623f51577a4fdabe52375af64863d09b294de12c786066a4510cf18737b5cb2521bbd58b29c859db5be07fa8057c61e27972541464105d7346270eb1eaf8183f00b03e6634f391192f98965c1fe150acaf8decb6acd8ae9578aba334c8ceb816f220f0a141a0b9c666e712d8917ceae41edfa816a4a3cc319116df04818fed2c5199163f3eb27e7de5e4d47b7a22a1e7e7186dc3a4ec5744e6b26dc0ac4fe236317e300bfa4ac8d1c52a22a867753dbec2a0ef3ea56b28a978dc21c6d56e0a9822cab56055b0a192a4f2a1fa9a1e2ad0d98fb85051bad9fcefaff281aeb31781b070f5b929a644a885cc948d2b8e9b7948b588a829b836bd61f9087f753533e5c0e79b5fc84e0de7671e00294fc3a78e2ad1e7d469a0e2cbc379f6494954a946d23e35a296d5c94f4cf03f663f7328be01a3aa375f6adfc6e3e10f89456df78a3240903d00116a9b3572d29437bfede50d55590b4df762150fbfdd6fee2c519302c37cdbd0ca0131da0faef83a2b57f89150a76f67a22fb2825ae844a2c0e55a62c00585cf13f7c290242ac5610faa0fee3018541c5733b68415f6f999951b90921e98735f1adb294593b17e6678722213e8c446ad3a7ac022936b20a6d77b0f6176f505aabdbc699bbc6e19d082fb8318459555069abbba3d01c1b9b552da6c8174ffa28093ab52aa34c5a04e72119ca3dbeb427c31876f8a012fa6be8dbd2a81076baa410047ec186a7ca713a00714cf370d06de1847c65f81e0023042b1f491eeeff34d3b6e787870dbef2665bc02dca4ec8c0fddf496de2e31c7450d626fd10ea411776b22e3ceb7f3891f81080eabfbe32548bb88663fa270ebba72fc8c5243643188bc9ee4d07de409f501cbfa49afec7c87d1f2789d2f932f298ccc33c0fb18246887d583e8a5966746fb3bae0cb7f3053e7d807c47ee679166cfac9f11031de5328aa1e8cbbfe748655b5ac3f7b303a92328c64d738e337e2c88e4b1815aa4e0f6573d470282ce50581e75a13f328a3a0b9a1c263f5180223110576da26d8a27a0e4e674729f076e96b629ad9b2501d8f8d437d8ed00e908692403aa12b370df72df67acd5d66fbd6d6e9f3341101c4fe455c6429cbdc3d843b52ae36215a43ac8698db6dcb1ce12497b9f51d8cd0c4b78b96bd81f24d574177a98f2c19090146ab59f7d5f461a0c6bdad1e75b6da38e9147b88a655ca582581ad2b64274c7a63e1cfd278616f7d185902cc57cd18f89bbb4ce2dd17cecebad3c3ae812450131532f01a9c5c62f6bd9dbd817f5b7a07ed301ed9d92083d0724cf8a2221e263a087933e462f1975e9d2aef17772cb92841fe96566553bf90ad051da81e0bb61346c75d7de2a99a99001e56bd9dbb08dab169ec147bd7da07e01518d6951476e1758afce02df80c3fbdc515f04cb5bac48c201aa8b1fe54a520908d01d71def68a4993955aee64e9ca2d4b85bff5404f51696a7f26ca335b112900df31029b365ff086b60ab3d13c2c44465a1656c82a1472c6471bd0a018542139b4bee2741b75a8797dca4cdea4df105561471e1925e46a05a495ccb95a62bda8f2fa905bf990e5f7a30b9ec101e503b1c44ce94d6d751db5d4d01adb185e7e3de9f27268e33e71fc0136c349931194a638570f3771419d2b2dcddd74580e61cd0e1d82651a4f74f8bcb480b02daa45a841fc3f76890eff255b85a88bc204442cc35b501e65b950d5618397f528e8835d8167c888594c0b3d3d56335e4968440b7579c18a71da291ff2dd20bff984b0809d0a72ffa9aa15e477ab40e998e1845754979039b052e98b768bd8ec0df44278c207269026354eefbf6d32494347009c580731b668298f1615b5fd77cf4d43bf2f6319db631859267780752e728fb52d39eaa1f8794c0aabcd93d68844a49b146200a8dae0284da464d105fb70dff102798180231d29917b244496fab251ab80197d5b680b97fc6560a74d4627bc8702da1430dd7c2e80e72a633dc52613da653e24c43173f0374fb9ee7c31d45109a2f2dda1a6ed6e02c387274f3dd41d3d30ebbe2d0e5be93a288321e54eaafee3ae3408d2a209a285a1286870d7081d8d7987d96c0d96ca7d38d53de6db74497ebe57c97239e24d134e668da1e614fc31b92e571228eeb9eeee087b5e87183899701652d1223a0c17880be1879a41e3e4444a426ec0e9fa4af9628907a42e4a40d9aeb57244d40642f454a684f07fb45974600f06e72ba7ae4dc8ebe05a4346ff12279811e1e8ac8dabd36dc3b58fe17eb6bda441f9d75fae5a431f131ef567c49334a2026b1b06d0d09e4dc77fd44f9bba7003e5f6b6b6a78d19cd2ddea68492d8d5cf918fd982caf0cf885d3a0819ffb8da3d24352207baa1802784bde523e1d4071772552c303070de53ae5a62aae59b858aed5260f66cf269d2fbe57df7c3960c84c00ce310e336aadf9d8ac732b8504f61775296afd57ac458c4b3f587e9095473628876de23bdcea9df2fefd44148b794984b966027bb3835f4deb5d0323e3d8512839d9d7e91bbd6d0b579a8269256fadf864f388ffca58afddd505f1009a887403f654353a693b5449c09424816a4f223999c0261b85fc9e27f8044a685fd97e21c348d3131cd0ad8ab11361ad8bd542e13b95cc94bfc6f4e02864e77bc3a5ed27eadddc4e835359c11b295e862d4647e0be8151f1472a2bad617d662049c258270ba05cc3c8fee60b79dbe3cdd5892d467eb1eb919b10908c1e6bce7cd6256f08b90d498edff9b990d45e8517e68b155bde445476e86361a62bfca66c782d9e1d5020f5aac68add0d8d5e11313bd34806eb1d07998bac8aac78045bf62ef35d1e6c68699ae309a357eb91767cc97f41afc839fc0e2acf149f03f5e4768ffe12239d5ec835b63dedd8ef257ae0ecf932ea82089a9b334f50443d89da8b9d940d17cf45a436d206057e1baac60dd3d4fd5053184b5fb725563e2f5dac366e47a41671e27bfa1af5d7604965af6f65f9cbc0e187826e3034396ad39269a7828deb009d86c1876e90e2bdaf2310456e63048c0ad17f98d7e3405a57c62f0891862c22868eac6f10b58ad7476cedf652fcb48c170c458ea95e0dab1300d1477d371a0a83855b2b4be0f913d533698dc00b61e6e0d98172e11cfecd9f2ec88fe7e1282a8e3523974469c524c395b19cd1b2ae2e6a0f08b8b7e9925dd16241984f02fd028654ca0f201085d96833d6c571a08c2b680b77e6374767fd732a315e12b8c0150e7ce2da2707c402b007853edf703e5a06659abccc5c7d539a6303c10a747a1e1e6c660a3493a44ad142c672094981fe9b0699eb6202ee481875332cde412924e4d4f54c75ba290478813ee7b7f8d2cb73c42bdc9b4db044a2c033356a397d2c4c27dacccc0604af0ab1b240b25d79bd8efba3cc58a7d4e98a56ef93e694d61dd9e5f7b64e66744fba85443990e2ed06eec8d4edba99ed139bc3fc816ff1e925b004f64a5e0fad8b437791220910fb7619b648984bb76620b9995c1e0a69a700c51f7b25bbc228f229520d7655200d19e0e155637260069e8ecaadaf56dfda0b606943398c2acd8244ef5e50f797377dfc6fa9ee06f4275f03912c8b039a4e10545dafb7eb5d56f282fddbe00bfa22c42731fd13dcd08237f88c870a6b02d821c0cc4c3cbaee07da622638faeca1c035134da588eb3a47298ff1f76c5bcb1b07713872aefcf57c09befd5274046db1e94bbf346a9618cec87c5d833b3cb6bd1fb1d5fe9d37ad9b8f7d69d27e74ee6d11442b4072971d6e308c90d2f28f692c154ec89bf6cbdc150ce4340973cbcaf071d3a6b86f43c96c95bb68fe435b719e204f06a549b9859201bfb3c8f674334d15b92441bf420dad6b8ba7ed93a4b10d2446d9f477b8f4ec9d7d61dd89ffaf24851b9e1af79ef09d04fe4c7d3b8aa5fc0da2c0013fa968fc23f85734436cd57b203429df6c86e228b25d897c432a70cb14e82c91259ac6b99a472f04cb05da560a649d84b1d96e532adbd39ce1d118e024519d5d192db13745d58bd16158c16f79f890268fe33acf3640cd7fbd97228a0ad8f596ad7c30f5fd9a30c3a58d741a2d1248c01cf56fb12d646351b1d7bdec09b42a222f01fd7c10403cefcf03c0fea4708557da361d24f964088d362babea2032fff91add268b7e2260474cc0c1aae1ec3526be33d9724af9298ae7e94117125ed9034842758be7da9d9f46146f157bcdf61f8f0ce96b32970cf633a17db02a03183311f34d9192fb47068e905383e1fb9a893dbe99e7852c091eb056d2df2a36fcf2ea043a1f9f80df7d2e61ed2d3f3294a2bd6ce92f31dc6d8bbdab4f6d1c9e678355d1fcf76296effeb331d4e2fe67a71da03da01f1a18cf069553eaaa13980e46467f02c69bea54c3720548b3123a35dc4d419d66c373b9089ce74d2f1c887e07f9068b2b4151380e03ab3782c4d3477698d60306b49d96880133708412e7aa8c948838ad2e7c574a87420d2eaf8a25535161adfaf0c5dc8f246e6f831f0b0d07d5c1786ecabd3ba2c3c693d53d95d3c61376e795acc867eeedc7c22e069b5d13c0155fb52b2f949bacdfedadeebf5db8d0bf6c78a11124a650873d00014105f5c4d5e9a76723d3c67939a223b0e21dd47410856f6109731f3fd4573914528d4d0451b6e9852c06874f6a8f499644f82ff8016134533e19e7543937fa1b79e556eb2a677f43c40ec09e8c2385b5e5dda6215ab070253c5d3e53b2ff7a00a2bc71a7012e60f4ac98fe1840e7be8c09aa648b3ab0f0c27085569d6460b8028f028a0fe35cbc839b5c9e28401bda72c3972c2888d203f378d76751e957740c1177fdc8d98ddc08e28b05c069efa51c8252cbe0cf818abfcd395484981c0e2b80cc558a8a10aab8b962daf92a786433511af9f8527aa2a49c2b8bd53101d105fc145055d9ba564709104c5549bb24a3dd91a505e47959f719258a1cc6dccd06c3b905097f428b5cff87707bd0e1ce71ece41f31ad817ff1f3d04630274e7707bac1c17fe327bb81353d07980eb53d4602c580dda7f7f15e59e57cd7dbd3d90613f1fd884030e3bbfcb0d7efdc94158660e2fa5c7d49d11e08a9b723087892209124075b7d82394c4ac6a6a3e6744b706cf7f538fbe24358c280b1c6d241ec04a10e305eb956910f0d968d0f83280049572e3e7206b91ca83f558ff6a01694227174a5061617717a39664d6b54a851e1ab5c9041c80952a220f62d183fd4ff02f9d499f6808334fb92a96a4a1332be8b96b264d7ace5e3dde89aa066da81471f8dc4f4c40b4ff98e4b85557de6e994baccc86846ab05cd2e6ab2f7affcf99c008ccbcbedd42d97aa344146ad1359db6ef1c3c8e7ee17fbbadcf7d53c7b749b00d57fead071621cb4046c8d9c40b138b1e897b30d7872b654bf975575b18b540e2b702474b75054fd96c52543d85e68751471a807905b532f7b72afa62bc86c08d20514704803dc419c284bc0b953f95b2c5c957979546e79b46c2596fcd7bb07804adccc6532e423df312c056d852c6730935f95c1ff85556161503263da9e10b45be2e0cfbcc72420fec0109fef3fc1d69a1610ae256cda6fa0b1edb22e6325599a344ed9500bb3b6db80b3ed4b856d798aeb51be3bacd83e3ece79a68d750423ee12cd58ed06961b157814caebef95e7bb0384213e8f34c3f364f7e0479b248d3ce8d2005755b5832407fc03cfdfd570903d70ab82f55e01499f0e533dff1d1ce6f0c124b0fa6dd880e2c715a08bd9834659fb5dad9de7e79fed5471144c0caea76eb72fd11995bbcd26b93c3eeef7bf4613270088c9d292c5b16a2b9c5f064ed3eb9f013c40d3b53e08486724ba493c06a4d9ec7d768aacb0ae7e9168c0028372c10a7600ef9e464c2617a6185c7f9f1517cf7925c3516a0bf6a8f6e7e229a8fa104e164b8cb328d16eb11f3ea40f968302cd6aeff674f113a0d5f4eec61d57142b4efbbaf733db8415459c129ac5721a1b1bea5a025da135349fef2e86031668421ede6358b63d5078c2bedf744d0b9304603ffa2726d02e8c9bedcddb10e57654bddbf2ff23134fd892ddaff5e13c7571c673578d336aa4cd6f7ca9f30fa7ff2cf4ea874d55be1e755b749a2246ca93b7bd613e24c52ecd0e0190563713f2df9406088e6b2cd92d9b03b4e3affe99b7624321bd6b462fbdf3235bcfe52871605f0cada9534cc6bb07b31b133dea6b0f533aeb70ec48236de198c427080056e3d16a420157d335b08369f69b8897645453e8087753b82fcf8522406be11fbef23cf74e343dd70144cfa5ee7e67ab40a2dc21902315b46772eb4275bb841254da0b779691c5f56122b367f86dff07a34bdbc053a9e56dfc4afd69fa3609198d4cc852a9b2707922e89d1b91ebb02dd602dbe9cdcfc74022e67aa0eac4fd1261d7f338e3f31aa22e69b5d4217900caf7606bac392cb7b46b96b21305ed0b0781389ac7d3e23b955e9358d57eb4735c8ebaad59d1f2349dcc16bdaae8c0081d29f21df52ff42d437137ea915aa96d1e286e1a88de177775f59064fb4a84fe06aa67f3a50812921752d0fc249387a70a15aaf42811dcbbcde646c49f404e8f09121fd977cc344c9126d5a8139cc5afa4c15e555805e2edb6fe4a26460990462fabe10ea395785faa72127618e810bdabf626a3e54f78cda87a4d3c068c09e10663bc532763b0861d40cbe1eb9e8733ffc701df12c7c72a824b1c581aec615a1041c48075e3a7ea820617fc48ae9a815b01b78ca7dacab62c9f4b32028d8e0e0d15d296790ffe2507bfab1653f0e13bc3e70337762110b30e0f2e2c7a86d2ac0bdeb5e0f0ec18899aac0ae393b62e1727b189591efbaaa3853735ea9073d90115f3212a4b7d53472fccd625def7ea36ec894cb964a6dfc9a7ccb369d549c0a125b1f2f6f5fd4fa21b90dbe622eec58efbb1705e26cc444bc5aa0671d1b68d961bb5706653c96d8875eed434702544d6381308c43e8fddb0a5640340a64a287b20ee4d26b56ebf145ea85a4315980f5c1f4e64f97d3ee7269de80f1d5947c1a61fe375768d34e3151255e55d0f6857e895d118541da36e13ebdf6eee9e981dcd0d9b0fb6f9fab694ba9973b04780ae2729596e3c425dc871a404a2bc36bf5a7300e0cc098986f2091345a77f699bf2eb03d648c8f8886e8e103faef891f1f3430d57c6d441288ca100d3cb75b8e3197a571fef30525f4b542350d71170de7b63197434f4adf3c4383e89d67c298c03447e65f8b572898d86bcc8df7d5df4110815a16f3839c72f3ac89b895876947140d4aee3cf1d7ef24a44d41703097a6c912c783b7b2a2d525b86be5b993cee26e637b892bd6233a46d0412808ed53309a126051ffdd0d5e0ba154d1f3580a24c5896d172b523c26bca9f2cb842195ff17803998f55cb444e663539aebb346cb75f5dd2f096ed1989947829c17059d40d3121f726cfef977699f9f5f4c66b9073262f539766dc48a053936113d6cb284ef3182d0a64eadcc863d7267978ca1d1afd73412e3fddd3be739603e57cb3ab538b2daf94efc9e5d3473bee3b714734a85db1367c08b05bad992d964802caf16e360e0e85d8f672e27ade6dd0afcfa6eb56f6181729cbd29af3e38b3b4ecc8a4ed724b723a3ad15890a0520a9cc26af542c1092d15000e9f050f8ace54f722fca9b0fae98c4af61f623dfa3b6547dd7c7f269e4106d91c1fb7362b98c1d36f73195116ba3d41cc2bb48e150c70aeb89257d4830e1c270cb936c127a23dc0c6fc7f62f8d6e4417046d79ade659637b6ffeba9b4cff1906a2088e5f6a9609aa704f9a1165b54e18ce8e325c8671f0a81f9263ecdc7efebbf9b86dd008d5f5f7cee56e1c021e2fe60ad190defec566a5d0bab3da01faadd8362daf771f33955181342d11e1a24c26b5d76a39cc1cf7536241ecb9ac2b798ded0d845d1a27c4630316c321b3545192fe684d5ac406677e9bcb389409a0bb05d0cada8421c5344a8cdb750575e263afadfccb386f60fd58489d0bb4370f58f4dc12936a0c1f7d94e2faa22b29adb0e175cc473c2b2b52586dcd28eeb8a244cc8cb76d5f6fa9ce3842a7f61c3e506b19db42f1e4cd8a4b05faf174375a49a3345d08b0c6544bc814d411b21b8e0930d8cbcd0a0b12d146fc1706a84570251f07d63229e6b54cedfe841b371927bb245fea1098b2d4893abe28bf9cf892f53e29787044c915920868935d1005f86006eccbb4c753a380a3f46ac1467db33834c5acdff2c04978e0457583532f0610d3970d4a9b5b3838447839d1fff0506f28421b675effc4974c3bb02bd22bcd664be35d9e5cdc1ec748617d7ffce93e68f835aaec582277ee1beabf7abe2c63fc73c84ea149425c4ae255b6ef597aa1daeb19f6d5a9aec78ca0264ed51417192e91d71dd8c4b1687cff223ea19889910882de0c4f273b29045dade333512c37500afac03dadf9007c95e3ebe1eb027df638d698a535a9ad67172499858e2032af8d2c6452d9b4a0f925e285a6d03775ad1ef89ec446adfb664f078fac8222af34ebfef8c8e5c5d07332510d9be767a2a8fd3f63fc719180097b492a444a1a6f28902cbbdd05dbf2624db029395faade8c1648f360c5bcf6f00969509f4e2fd5532d79e87172cce7840ae5b302068c4e799bff095a94ee7ae5461e52bcd22078a08923c52e18c127deb635785ef81c67e7244e64c1c377396736dffed3ec1692cf5b8c4f2ea3fbe22fe38682a641486f2c458d951ea9d5fc55680cedd00803fadd8ff3aa101e11c264bdfa027ca68801f1f4c873b99f1f3b4a40abc376a800d97f88e29c845d8d9046509c88f955f9ca06da4815e00533890681dc0fe6733020b784d02b21d43155fdd5073bbcaf3e1566bcc390a9ba9b063062a48a9f3f2a5d3bf4bf23bee163709fde91c96de0c5a68fe89f2efdc3ac38fcb7c1db40c61a51a42e9804b0b1dd73cc84648a2281fb4fef0cac1feb14770e9b801ee189241e0fad6797d640cd9cf2aa1f55f4907794b6a3e4090a0074f1649e835d0f72352f14bb17efa94ebba89f2cc195d19c0a4482ec507bdd852b55c79e9d8c59b6bf0ada7730dae0434db913ece0a06828895b07f39a3e6ede7e9b2c0e1466188e1313c67ac6c4f1f57e5f5557f09fc8aa8d80995ef0505ab76c174d775f52d340d313f9b536af11d44cf8cab2f740329ac458c884671dcaadc9f91629eb57fa5e220f89ead7fa2e99f3aec56bd785433322b26e444bbf3b9ac16d3fbdbeb5caf9881cfe4342d8f74ddfc2b008e5e7f36a2441b8f69eee1c4b9b5920e6cbc1adf92f2d06bee425899d3e1103d87dcb7d890b87cab8fd75dba3d58da32f6b43f6b72a1c68b90a5619cc74439f23f6bfeda6e7a8bdcea0174649192ac8a3077f36e7212f0ef21644f05fbb965313c6b59f1a3f0e5c1eb66a2e4d73fe198551733e78562bb24ecff59b56bdfd4c04ca5ffb49bcf1d8eb8f47561c76454dd44b47618b57011a491c2c7b0c8f582b70a6dbd8e973244bfb585aeef41986969bc766b04dad2b9029f14ccddffdfc94c9c51aec7d3098e666ae5a374071a1d0ebd51e1a2b0a105b7ab3c6ad17aea375f660802b5fa962f9d7c641ade8f21d8419cfe672ca5e2881b2d7d1561136e96ca186e48709f2a388aacabe3793580b722c8d112a0bfd1d0c0af75def97d5d4c73a5cc62be153242459dfb1acf02bdebf66775fb3c52326917db73597a5134cfe5c0aff32db8eb8e8800ff2a98b25d2693445b93b94201097455751c08f78b7f961ab6bb7b72286b293c77d219585c66f12c49921c0e90e4878e1ff88252d4728c52613de28c758fa1fbd8fe1c563055120ac8565f85f0312a3c57ce1e84512a7e6f01d38bb3649eea2b549a89f4c7c9e8d048c6cc5d62fc8399a0a6f85477d1a11355dfac6630d01abdf9ae7612169430e0fa9d77c2118752bb4abc4768d0e78c8c1b258206011ff5114d300e5f9cfad20895f4ecc9a0625adf0a0ed53181adee44f47cdd416e49d035feff88a30d11e73e404098a0b2825d765e0c53f5ed8b6b0050ab489e5524616bd7067728ea8ef25bf42ac3f020e4f9b1e6deaf12a3336ece22c90458e376cc588ec99ad2985536bd510ca9f600f856a8db2170bf53339701ca57eeb9ba5bbbfcb45d3345eac67b15a9a7d845716df6841e09c71346046a67fcb5b51be9129c2d0faea4eea5f7af7fcacdddc530aef1e9d1981bd9a78af2a0d502bddd3748095d619210cf3aa8705c8492e156311474629244272eba57adaa7e846c3fd8fbbaac71b25bc3feccfa94e205e8fdb02e93809f7de44c8910070ecb3ab1dc6e088dfd54655f84063e4ad7caa07870e257f213cb9ba4e34e721d51d8bbec887896bb40502d0f4646bda6254059093df14e661cdf5d9fa1a86e2f31eeaedae100990227762b9f2d8ec60afecd697a2d02268e342042b0d69d47305455a40599e01a483736fc1d9becf554c695b07a27a27bf096c3282aad2d900c6b7a8fad55643307493f8f5bdad4c3f8d723a97c8a701fb4e51bc063eb1d8bc47ce98ad16ce5b5ffd15b5f7074ed14e9861552e994912401846c12d1ff7905485680f6a0a49cbf4e970e5e141863a92f64c2bfbfd1a1174f603d2a3e484cffbec5010c3525dc2c6c8defe0c84bf8fe3cfeaf297ea30291d98fa5c334b60f5b157e576a25f1bc5ffadd50da6da10933815c8c19605ed2cb08b7a1e4fa23741de312de8b96e8dc72c45770535a0225522978f46cf1ed21ab5f36e2809661362bd7d96bf9a687bc852ee38c59529e06742092694ee965306f7f63faed26003a1b3a0722a267a68dd9d8336bbb86153a483b6be5e6d09c82d52f1ca592a4fcfc91aa516c7b437d24fc0cc47ddd3c783900840ffb419c37d28cfc87e9bf208439bcba9dc1b3d8f3a8742bd2d40dfff4c47a402f93cb70ee62ac421452d7f50b41ecedadf39bfb01afac4290ccdb2c506570a3bb837c133ee2b7bd5cdd4f38fa55aa486fffa202bcf0bfa43707f66d8d6dce1da6e9f42f0440e81894e21855aef5b8a1099064194de5d0497407309360e77ab62298cc6a7a7171617950855fa6e0cc6d3b58d131d266a956d1487a5e73347d133f3edcd09f0298a0c490d03d5fdf00ba83fe139d3b3c0eeefa89e3be6978224b59a8a76751d077d4aadc1d2c3b7b8b03ba7c0d41e798665b99587a1349585699f793e882c17151d330f5a5bff59500819e8e6940a17986979f5806dd2f482ab65d5535c42510937a1e876afbf69f5a829709008a489b9424cc64a5056a6aa6940a88b8bfb1b0022da31d46a05fa6e2fe2f0ca37b71ef3294c374da7f869f5c5195a2047f2f52c1606a44856951fc2029a1c1144932a1a8347c3178f7353972d05d6dc04937816a787ee7a67834a972e8c73651955d9c2820d0941b2b9703b7517ab89cddbca15c281d195c64d5968e0b04f50d10f46714a669d7b5b44a498bd3444c78678d219ceda4cea398aa096457e1a8c148cf86c655ca7e1701d895f3a6c35b9e43c95305b3ef3d50f8b2ccb50a77f64ccad9105908ebb80a1b8a2f6a4cd61fe25c453133f46da139b495892c89059549bfbf6ebf55afb5a56936f9e0c4deebeabe56f1cef15bd45d2c8c4abde84c0c5335bf56f1f298106b3b43a002acf52f1bd330825014e510e11718c2925f353bcd7f7e8b2b09b57e4e14ff26b88b6fd0a94dbf308f688b7fe963cbb6669ce06450fba3bcacfe8922a30dfb5c4c43ed85062969f155d0fcf9eb57301a20fd246a091ddd91e58f93c527184325ebf94c262fa2110053485c49ae2de63239543f5660d218f531efaeb5619bacaf6ec1a531a6f3e4d2956f3fba0a2564c97acac533b19e50c7f1ee04e0c99171bb2dd2e62c3ff384f10b21e392f556dec389c7004e57f81976a8638172ccef2a2579de9a2dfefcb670f06a188216fd3507683b4225cabb350fb4debdd0955fddf0af3edb592f89d5321f89e21848a664a152a2f10cd30e5c3062a73f01fac6d93b18df54a98507d7b24e1a17e79d5cd7d7a04b6cf687f64a54f769ee6c3e63ea939943ba3ad71ba7204d2d142645247e71ba82abd6d8417e3e2af10c1744f09afbdb83348b8df388005fa197a28bfb1f8888fa91ac2f37e7c50740639428decaffd29f3d04c45736f22ba59f6cc8c9b9312bdaaf2ad0eca04c6a8ba9e666243cefb2349cc162922e90e82555416ed28ccdf080cd47acdab87e3d04fe234f5b61e56a14cfea2ac4d82142458d38c7226bb448488b603508251114422bdf608ade285070d3fb8fa19be1efadb5e98dba8fe4a0c9bdec550f90ab2bb34ed5fe5ad599d3edf27d5a35dae2aebfb3d296b07cb8f99c4a1c206b0b6b59271893eb9683fe948e6d393664831038ade85996477320edd744404e4bb9f874a5c13b49d2f690a5decca2551ace314736f8cc59be9a8275245c14730b58bfb696f7b903f798b87a17aadff25ca8217d71f6dc06f7292dc614651a3a12e26d678f12bb20f594e36d8bfd1eb61e6562221acc3c70b28ef652043eaeac624d8d1680fba70ef5f8d6e90125087ac8520075b5dec10f13ecbe75a01ced0777297615fba720f5bdf4acf77fbb61fdf6f85281b7cb7e98388ffccb8455c128f6c741d1371e4eb77ff31254000c5c7b986ec23cfda8e2c9d14b82ce9c8786199b8fd5803c96bca8cda0c50d51a31f971ad900d6fc92e57b5c54cb1628a4ad1caa3a5d00dbe25028a0dbd729902088213881229d5a5053a0191ca849866e654f9bd5ba96aca03f1f4124ce3d62b30f4e7998627f297d970ac0b1b53d1d6f6cb845e9c5ad21900150d743eeda160d5ffaed2ff2d85c43efe8d4a31c58ab21732c303232c3878801a379941be034b86223be51713a14bc8f59b8f103095c109e1ffd7adfc4e6dd8f8b6a6aaef1533c482e984b3267ab716b1185ff128ceb78a0d6c98cab05593591c276d0a646c2e4e0d1df6232b8e4a6ac844c7e6eedd02cf16525b9cf067ab4297d849911d9fb588bda630212f042499036156d5eca74d67ff132476867da3277ae180ae38530a23e2ec291a03046c5c50343b4168477e8b03995fd4b52921f85905314d69292b0dd5bacf3f1dd8a3d3211d6021a975cbe496fbbb92db2d214ccd233f12af8bfaf2593c5475177e16836f7ef420d8dde0217b20aaa70b79fa8a173f7ec8cffc3f3d741c6ec11a097061ca3fb6798ec89e5465d56f9f6c56a0f34884f5fdbf17bb467fc428482c033080d26b788806d4be674826f6cb45272eecd195719cbfd29456602e931404d8e783245ccaf9ffbc85e89bcb0a960ef799adfd5306f5a11ff98d386c580b2104a6cb3d665f49103cd770f2953ba77ab813c925e1d3d1befa01320f989200", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x0000000000000000000000000000000000000000000000088faa138749b3265e00000000000000000000000000000000000000000000000ab39d7744ee99e93800000000000000000000000000000000000000000000000b58e3fd596adfb3f900000000000000000000000000000000000000000000000000021543697d80ac00000000000000000000000000000000000000000000000303b482f3002f24d800000000000000000000000000000000000000000000000b85b3e53031c19580000000000000000000000000000000000000000000000003e4b73366064f452100000000000000000000000000000000000000000000000000005aceab8040e500000000000000000000000000000000000000000000000f5dc05a7bfc973e8e00000000000000000000000000000000000000000000000a5d05f4eb90bb6a3a0000000000000000000000000000000000000000000000060872b59764066d290000000000000000000000000000000000000000000000000001421a9133d75e00000000000000000000000000000000000000000000000f4780863c86fff52f0000000000000000000000000000000000000000000000023927881880d93150000000000000000000000000000000000000000000000000dbbcf61dcefb015b0000000000000000000000000000000000000000000000000000d0ae97d8719c1b55ed60d28990fd534c598c83f2d728a5ecfb56bd465a081d6618cec0292cbd237512e8cbd2be838836717f3a7d3b973f5b990c5a7f1ec421ace3f47a651c5d00fb5ff8417a44f464db9e3db570a7873bd28a5dc9a6bd7a85067e4836b68ce825c611f8f6ba443658991339a5094e904964bb9860defcc05dc2482f953fb6061c687a6373bdb5019e78346a3d8b4aa6358b4b56c32b61afb53bafbf7c51ff7415b3a062e79c6977d6d8564fc067ebddac69d8e8a80bc352294d758b31d13e6c168803f0edf6007b840fb864938af01a0d54ed66c99dd832480f57cbf93354d92dbe31fd406fba859e22d0b8c21ee8ad4357f8fcf75b97fad9e3616fce00d82425ab8ea7cc24b56a26eff6f6ca0f8f37eed160f45b06fb8a36fce6ad6d74bba4190b1a98f0cf95221281d936efbdbf97e1b85c57587a3c202109bd02b0ccac1c0865a52812ff25c6ced7a77eb02dc8b801760bceb81b5dd81497fa55c232c7492bc6317bcff50690bc531833b8f056eb38a2cd70a6593954e78d9f2a1cdfa67b0b906a32ff83bd1e5d632f37b869871b08381ce92e74ca749c5fddb7ae40ac0a0f51a01d2b5f7a67480c57b125fa84f186fe9f640b7461b9a0e35abb212f49cd209f27b1113fe34b0798d1c95cd6e28ebae33697debef7f295d707aefb198d9a0bc0e4b323afe706a0149918f17bc9ffefcb1523a0debc9a18cda5beba59ceca0ef0267a1b20842c607949f2257f3d70b75f00faf629ddcbdf935d0d0eda232b01ce76a7013707c66fbf745546f19d041fea89ec58d214f41dde90912477de87296d3bc7695f2a1b16e258c9525cabca394907434b9d2541d5b4ba692d0bb486213c2e5d7a4361343f0b069971fa22f76f36817ce137b60f0b5b4f0d1be40ce60e54f03a0b00effca458b4623092ca8a33bf27a0b8e9772316d687261523011c1e8187225b6ff80a03bfb97eabf1a873051ed965f503149a705b147d3007de670c317fcd6a7a516bd50c772891e2bb3315f6c2a87c3cb1119cddc826f543abc71957b0eac5663718ae339d51af5256268d9a0474a403a6ba3fc81d52d89f71bd2f831f8944aa85d6d3677e3cc4876aac91778000ccf21aa14ba977af5a4d10da02981669450d55161ca8f053a871d81b5106abdd77663f1ef78bbb0eea21e2a20aaa868967a5907ad5582638c573682a851040e436f2e6c9733ee4896a86784b2ca0f330604ba7a3c5b0b746ee42632eb4cf982d4f3c36ec1bdd34c8bdba023b0d20997d570bb040aab67abf15b560b7c04c52513aee47c6646503836ed35d6f16388eeeeeed374906d0dab5fd1dbf8e291cbec2a6927e9bc4c052c66a9870730b20ff69314b4f45b36eae7896c08e5a06e691c8449e48dedf97ddcd3d674f0b0887ebb0c7f788fa577d949c4de8991658cb0f8dc1af2af7f58aba794610ac002f87749f9d104ae27ff60d06695572ea506326c7e6bee213de54288e8bcae89c0e5751fde4c6228122ad8ff186fdf8bb495f2afbeb85a95ee5cec68db708475d117aca0d4cc27198e0fd43ab5d1f9d7ab9de2f1a36e9bf112589daff912cdc442ee6e8aec7409a15a5fe45e5c201e3a052f12e53d1a0e1ce8ad6607c91f8e9c503c1f9c286c2dde7f8d41abc15a6ebe3825163ede6981ccd6fc9b03112ec426a1fbf2b27f200f4adddcbf200880be7f5a73d27cd24d09e4c6e4d111fac582576079d882aecc30f178bcaa94f8eb567064c9ec9ecfa99034620d0406025400b7a25dc35566bb590e2b378dbaaaa71fcb2279e5b11c15b1a9c8b286b0d1e7d3d611a84b62c97b13a07825b337b9b57aaeef4f86253f4deb5674dcc7a909b0ba5a62bf59aa6a4c18e6a975776383169d14695d6b9a3d305e9a21e745b2b2839515c0daabcad6837b062b7d01dc1ba60505955ecb38d334a523d9b1dbb7ebcc565300e8ad930df265a9e0f95e724b5f713967a976ad3ff3430d23786c016722106840bc0c54b8ab48d4f17c802901bd97dceffa70035d35e38d5ff183830fe90caae070aad78800dfd19c788ef024bd49c9ea8160ac550d117d074afad668a4f6bd510ef8a176328fd8d356b31ca7ba711e3afe130b93dde15d246a37c1270b6e92202ddd3604ceeb469bf93dde8652ce468c184857e67d38139cd669a089aa6861e065c283d1803de3005a23494532da0146ed64b85e40b2de3b63ce5844b29b1d404103d4242b9372da3ab04383a671bf91156329c8325f09bca871eb9b2525bde1e641d847edfcd96cc86cdf5b50944b2faf2e9117ed17de6b5ddc68fcffd88c81a07df6735796c3d20ffd3a78ec7882bee8143ef052475edef9bed6517e3b52428923971ec747cf4f5b9b176d23a4ed0119f7a43044e08f766d367b5cd9ef5610ab73ad97dc6bf4d90f55efa1219facdcc830df23431f5629b286602d4ae5ef903199317dd1e0549502cb2eae16f20916a284e792e268e3b7a57b556ea3a42e9267e8565bbaf76819760ea487f2b6442945c0343be29ae94406f3bfc0df8fddc08ac82ba23e9335dc9f9747a73ad89f80083c64e4e0ca3c3ec5bac16c5c4b98829528096d11677e5b2add86d8d3cb2ab4820a27e6b200c99cb89696853ef6f65194da3ed8a69e1e02e7c956f04f25d64f6d43196af74edbcb57c753c75bad26b046355828fe4b56d8e7b094de4c5284acfb686fca6769822013917b3bf609de92f0cd0835181c4e1e9c37cde2a31e9b44c31b5b6404a6c21cb03c5e2ba6abe4e240fb2bfd29aee712736279e486f8a437aff7e469e85bd5897caad3b28a75e0f2577163a579747d38a674aee66657777f9eae6deb89fa31e82ff1f2694aa389b0d32280e5e10ca4652b0592d2f88f3f966aff1518c47d4f7c9de34cd5193199900117b78694a8c54fce6ee189a22d323e097b90b66f4e2f8012977f5ca861f02178255d84c899b963c3307fb1e41bf1f9200de68ac04bd3042eef9907817e6771daf9d3a34117c81a9c27021cfc836938d33259c9d272ad7d1e7a273b7b54c0a152b720fa2026885f1f12bf2656c147cd9c54fe4b76f6cda531400c8178ca9f2013c91ebee44600dfa4fafb44468f6b425e50c634a024094f8cca64359515839300d925b49ff62a8510bdfed6de3f229654068ce3d956f0f4773106f38d8d40908648c87ccd1c64a2d1e621164c253ff9472ccd8c804a718b313952b120e2488267507664201bc55514d572a0c99e79068801972bc67b062ffaf6db639d590000bc5b218af736380dfb85df08d376733e5f09f473bb3cf82902a0b1070756502076279e5874329df3255ecafb485a0df06e08147fe1f3f85ba6070df13f7552e109ce9a119487805b843c229b96ec320193763785454fef9c984ead471d8a0400a44f3eff7fced4690b673718372c28a8b6d6924d2d0ffcb7cd85d77ae1d7e542da333e9ebc4a6d3d8e4a18172c4a282c170024b8be7a889a57fcc75c1486d7d1b942c027af9c7f98beec95bae62e76068b236387c0f10d19c4afee1274960cc29b78f66787e9624cc182ffe8553674830347e79f7beb68a8b33debca0791f242dd0ad1bdea9a27eb052b4a873a88e3bd0f3be38d6447938527ae035114eec551dda82ab01c2c8a87c78f8441448d17832161b30824d95a71b06fd78b051c04f1be9ad79e03364982723397fc744222f42a3f9e50a53f14d94cfe4decd554040033d5be4a7665044745238a59ac3bf71264854501f0ef4ee5f670690d814881c259967a6df24dc395e0ec1c7b9df807e2280a57705836b38ddc025f3ce8ce6b22c59fbff66af05881bada0c7b1db9a5ba9b601b487b49a9fa48678e51b413b471c1e72a1591e1277aeddbc21c9e887e8c96997880d93b2865a610c04f470dd771e8dd57f4d99b31007bde7601a1873837f2a88c0dd1b886c1ed9b7b411393e1800753e85451a0a3669c9f9a909035e6455bcf5d67a1bdfc4aa243a17c16d6ee6100c493ceea0130062e564d26ad2584491a0a7e215674f3ce0aa5b68f3e3f80f02fc04703bd2c4ef3f812bf23717525e2dca862c640765dd9cbb0a897166ede12c52f69ad514c2b963166d8a0c16223a50170f085c3cb1213897891a2a4d0d6f19ba1a0b8e7faded21a7be15188a512a1e6dd25c9bcc6e6a31ef31cad5c0cb8e1932d820675400dd30942d518df2005ae322f8f3190392739e0b2827b725ec9c2d6738d204989cd547ba1cb3bd37ababc597f124283ddc47a82fe8a4fa89c181082cbc4ac86b67bf74625314994d870426a846c6024026b841efb1b1758390e713f1ca562c70d4385dc5f424727419fbfa3c6632ce96cddef10b998450a8c6f601c4418da4d9e1e56c4eb86c8fff01aec4955f1ddd3fad61f249b6e1a74e0520226c7f49df1282612cfbaf389d6f9081269abad88c9915f8329c6f7f10cd30fe22326f40b9c864dff34ed6c32d8934e1d7a4f42a2cfefb8680b1ec3ce5c91aa90004db4643e5d9b5333344b28816904858056b89e854b880fe2ab9c421bbe47d2c8583c3567a93087fb8a72181db348cfc076505940124d9c48723976a84af7a24568430053eee947b3d1d420d1d3f63ff8e9d42e558ced36e2e5d4c70c47d9b28528cb14379044613b775464e86f94f62e29058c9869322648535ee9bcf8f0b12b1ea3acc204f519f48096e19da14a408266e0c75a2f25a47aff402f8205a562df0af8e6b8ccedb6f16128fb2d577d884101b679c954fa42f4ac6715205efac01ae7bd1f024c6132107293a1553365471c18c03ed9bbb3efaba7a9cdcf4afd7116da94d4d486381dae6a51c4554f36a25179d759f998fe4827fca97fdafcccb17f7c3b9b29db30c16573e4df5197879a1251d3a20015accb4d22271892b67442848049adca18dcbabff5331e409d702b9f4ce5dc8fc6efd52d0205a4516d9b40c64c134bcc23d7627d66edf1d4cd52ae985c7acc213a6686aa7fb98a4cdf9cf2d51b9e05b1ddd31dfc6697cc75ccd917f08a92c8ca8c5faf475f0a188ede9370adb00419489e89e97aba84aa6c77d951f624f9aa99955d56625553c3e7a14ac2f1a9afa3f34f815507ead2a646c9dd5a23fb9fad905ed087cd01ecafe4c903c22aff103f2a3ca614f98f8542b65547bf55a39abd637ebde2b6e437a98f54fb517ce908abac19a16771f8e4ba0887e1b3597b168235be0d6c60d3317f9f792e60863b4060fca1d569252f592e4be383d4a6e86310499886aba36263aaa661f701666c210f6b18ba1d8923a713b639129aa452867e0a8cdc1ae0018714ac7e5890ce6651f7545b2d02d5786a59352cf669926d36a6e3f754a84edfd343d7f88dd270c86f6a7c997e90366731500e3fb282d8c87e1c14443fe2ec4443c7efcc51f1a7f9bf1ffa1be301c4fc73d6bbf6a01b5340fa3d004fde4da36228cc8018b8e27eacdb17bbdd7264a4afc92d63c6bfe9efde14b06ea85a69d6b3100ad8021fe0775f8e3026bdc4607fb045cb447f79eac7d124c3c42a094f9f606e54b2d35f52f1c9001bb4bd8ababc8b16501d01ad354ecc264cab305e988705243fb7966552bbdaf95b7047e6266edc6b12e49e88ef2ff364122afeae596f9ce91bd35073214211c436997a4a33d6fef5e70337e672d8381a93f5b4ea1d06c51c21cf2096e1c41634d00c90b794087fb215c836e4a64055cbb9d50de11ecfd6a1c3060663b2b2e216d082dce2579c3994ead2afe49907335ddc49d4fa813729246dce975da1681aa5fde9249e2c1c140de81649f13dbb6e19ae3a9276ea9d0a08e1750309b0b5fb997116c1e20f25234b03c264b71c4a071bbe93b6d0a69e96b3b8edcea6018758a2c7c350a89e5428f07ef709b5bbd06df80ae113dbde9e61c9dbc99f9022595848e4f0adc98fb3c5d9e954a6ebb28eea6929974874e43277ed5837eac650cb979f976b878063d0221e6cb4822bbe42daedf1076495c945aa7144ab839c9010f5803e219631b6d3ec25d9b21c36d1c2a68efb4d8988bc82e44666690f1e8141f3fe11884f2331e475ba32ab2828b967bb83b575028e4d557953ee3b31ddc20cfccfd83d09af4f819ec755b026d64700d8fbff93d76356cf1aceaca758b3c0938ec673153913cd1287aa0ddb0f8c55cebcb8b6bd95e693e32697fd45f0d7f1d346b773dbfa16ba8917577a6db93867c5dfe647fc231a750f07426e89c4f3d200eaf6e0111401ed6742c6aad55fa225545fd321c94f5edc7feb241fe50fae82fb8cc5f9868d4c5c036e2cda9da4e93057ad89a8b90102b0c9ea0ac2eb9d5501113836c665cca9810f9dca35f399ab192dcbb0e7519c8e948014873cf2ca61705d8ff4fe49045287dbabf5332675a57e14ba2810f3e537644190b9fe8dd2d3120d5b7f4fa873c1b794cf3eb623d39e3a81d8f20115bc06dc33bb3eb7e353f242815891e9dcc9c791ffc59e065f8644e2de3f017d563a9b890905bebe871ff65059ecbf4d0d89724072e1f589411113ccc8d23ca44a1fb4025fc2e668a46b8ee14499b147e6396c868861d8005d999377cbda38057848aff4f8cfc8e8dede9dc28fb01a73e4dfde3e362611ddaec86726e945e330c5c20a151ea891fe74467b60d80010d0eb3028bfeee573db93dc2df313c89c1e545cee7af941eda5eb8d76b0d7aa7561c776b8398ea77ea3611f65571284075419c9775b05ae1c25cc9c5270c3f7757b225fbd1bbe5a0d8a96f0a5712ddbf0e5b143e1d4792317599f42bad0316fd73d3a81d365df0237c745529a2dcedc0d7b8f02501807d36936d8a54d42f1757d6ddfd411af39155b234e2ca522cd89a0759d0c9b3009c21e43401e27e22b3bcdfac5a4bf8ee81b1a1089eb4a643126ba01e0a5ee69879e50293f1a85317111cc292cbbaec3354d0863b0707b0cf8da5ca077515b36f5f38e87e4fd70f0c4ab1f4726a7cbe45ef9f9c657aabba65cf75f300f8b159ba9445537e997d1c0a38797ccf19233d14cc678b564aaa62ba7d6fe01172087176fad6c022a03b790b771c9fdabe10966cab2c1f84d6306d5cf48bb0347d80648abb1cbe78018d9c23d23b20d882438917282c6f58e00ad0bdeeddcaa14e7d16afb4a43c54369156120df55e8d76925bc74e69051b0921dc7b0d875c31f39c25c4810020ee8e84c415772d10884a64cd58b1124838dc77ea531ff6a4713ff7df0a33ca14a524a1f526e18a39af4a43224fc7a5963aa752dfa3bc5404e3618c253c596d477babc8da2ba0faa41b3133f67892399ee860d6d5e8d9ee061e39abade8d637732d85f21517083241064b003dbc1841770c5d0bcd04fb2503548694c9d97633514ea2edf6105ca39c7d4b0f3d36fd793044be89b2fa706d58efa0bcb573cf9abe4d11f83f0c183013b5811ae5062ca3bc440b364998342c465f1668d4bbfaf12aa6f410392980d6dd86acf51dea00cba982be6df81d2b8b4cdadd98c3bfb9a91a771744250e604c7d24c359ab15538ae8245cda8ca13e343beb9a85573cd96828039985b30d246e7a5d651c10d38d4347fd971cd456cbc9e78092ebafe7df820a530f3a5c287b615013d1c779c150c58c5e31c09fac48cc02f8efab3734f8a832af325784074698dd42f5b194bd08879c34a6df579956489206b6164519d972288334d47611ea33bb49bc107f82c2345721e796f87f53d326e1ac09c4f18fcab1713ab76d0c7144690b06cb2dc939f5211d1d37a8ceca6f6525adf710fe16c845a4fc7257281b842c19810912bbb1181a95664dcf9cdba87933d18a5048fcfae7664ac7931fb028726b00de1f6167b66edf4b3f8c745d12ab05fdaba8ca21ca7af9da2ec00659d049dde97aa49977a67c603f4c2bab7438f0bae36297c1d9aa9e896ccb361a60e0636664c2e48de1a528a045b3a01560b636d654a39e0dbe256b47723048261f4a59ce977e1e501672a0cec140c227e4aa4ede24653dcc45c5e388676a36072c7fb30172fe6f20ba8947997fac2c3696b3ba00a0d5a72ffcd5303b757baa093f434459a7ba88638f93c652f21efecf51ac4bd77fc06754036ced23b37fed22cef64f7cd5bfd1e0dce9c8a5da6d2974277eeb9c58a9dc3b3c6377f306114421d1197fc743dc2702a514ef2005ed4b34827ca67956924d3dcc86b7d9b13d340a2d78116c10b1bf75ddd470444bb558272220216fff391bf80e073927da0f0528c0154b1ead201809b2fbb9e90d21130e0de106672c7ac9b9258ee31b0ed20e1d2a3ad2f14524dc6dbd6519ad7669c43718baeb840b4df0d60b63ef29e6e9dd2a2592a2485b3d08d02c3c8404e44934216e34044a22759b8b3728f223218b5f295780f9cade7602d5ee2f0e082b8496b6ce04ba731f12b75cd0bdf676cf2af817639ccc9cd465e586fafb46aaa741e92e1261e6b717887d3c59b46614c6755012e169cc4747a8ad9b7362db7c3b79961d0866bfbf14d6d6183088e414c4b3f81761d6c503bb919c930b6fd5183919068902f359610f08722729e69e6688f3710f9782a1fcb7c9761bcabdd9797daab86a468f69163c49e59d931f3325cf2313194ef9805a4822a4426e999d895f9be09611877935c4de99732a2e1b995a04bf293c16f50dbe83edf3b65eb4f1ef97f388090f68a459cc0418b59f4aaade9b54115cd4c9cf9c7cb92fe3eaa5773905f71f9d1be656454b9291abb50c854777260b0765017ff0a27eb26860ece0b01d7c55ba4a8c228a82c9c5ca23e13926c5cc1a833344934d57ffd077d9071e17feea84956531aec7e05bb1dab9756ae3e54b2cac7af90c75f6375648c2f8b674bbd9b185e60de61a8a394b2bba945c779e890ca1867869363f0464d34287fa19894f703d397a8d6a458aeca9d7b4e58183d80a8b15dbb1a653c8682eb2f4b5b15a2b902e4a5dd9a7dd0781e66f354b81a10c2c6afc3f1ee3f06e4cd03706ec2acba94d3cb3bf6f922902a175e7e66c7e72d02e8826662e5e9db072f79431dd1512a7a736fa613a54bc5d0f48301bbe05138a07f996452e14dbba87e625c5c707453408d57a90b5e1c00b32d529124643903a1d630414afe6cbb4844f94564047eb9aa90e20e6e6e8e613dde4522a110711ef246c165a05721424b72ad332073bea9cd9fecd0c17399707dcafe6ed9bdc294a037e9338dc66c09b9bbe36985c5b021b32332dd917851e19901e49fe96834d851f697162287487b87c4551915ab4b151622c982acd7b0b9929d96c94229bb39b294f5b3cfb484e00e734c1f1cf554245e8ed206230380af73e79c4db31bfac732de6f61ef04cb748a47dbf487ed550657ff86e49bee29a65b1715a2a7e5d3ab32e1b1433ad80a8dea89f7575572699057956128d7e99718f00c6fa760e017f0d2ab1d11445f18995ddee563f4be7edbc55c00ff911054d40b1a55aadac71d64609546a0bf7e60d23278f4f271af9f104608d828ddf9eb57be964fc18f864767121d36926304da1c1a148a52ed35f5e1a3f6a51829d2e9229d46397235df4a5040cc43d240a26cc55434b407257e196ada5d493a715143f5cdea8530e1d45d1e92c391930f76ad6d19ec4ffe89a0bc977aa9ab20c15ecc1f20258e01a98876cf12347696e548b31dee516049b98cde66dfe19251ba1ff94020dabe324b200f0a7050a238845983e210c9e15e8f3ec7bf2e6f18725e4473eb3dbe7c9d3672183260b6a78bfc93809d22bef74d3565c69fd13b8681d6a11c06d73dff9664c1ebb58254edd9a42b57c50d0e2f7b95fc10c669079664ebd96a6f6b7f4d002a77ed57c04d5df10845ec831a420a1c982975d1a6429b32f76e6317ec8786511386b681e25cb4398b7c549a0c0d9a732eeb5c6764e4f87ac90416fcf22b3af1440b73eeb0bda7a31c39cfd50515ca746a465382099ad154f57486ff330476565c51fdaec116cd06f12135c0e45eb7e09b570a61f018e005b23cf44e6ff57866c9c8b41fc0345299c562c4736f442ae214374c27d57b305127384f6246e3e77da25cc699f1154465412f4241252c9de6a83d1d3d3dcccf0698509119b402e4ebc99549d431000adf755ab7bc2e91eab518f9a869cf90437100a41efea719c022f1112b0732d92aa49a2e0b61579304f87fd5f2efc52e7c44e7833d768b2235c7a2d8f9d2b29f45ae0e6a7e4310d26798a38a7eb97e32fc9dbcfba1f7e21bf9558f9710ac719295105d1f67e56210f5b92470665106668ef157bdec212cae3e9d3ff5db6380510092ba62e722d8e15b63dfc208f0da6da7b7b8c08f1fdb332457579997dbe1a55656fb14af1665618bd99835f2cebd09fa5fc1e2c6e7b1623306fa3cef8222118a484d042c7303a487143b51e3f0362149b02d18386da67b3f2408a8d54600334f79c6ddff8fe36174a01050d23ab2d4824a4a9d2a83a72e9d7a5913341ef209237b71535b7642d53bd9221acf76ea745cbb20214b6329b07d4ac15e330af12ca05b10c4ee3534cd732724babb196b5fb9d10c6dc91e1ca941bce024206b625cf7d37d944300b328be339a3e68f309e9b49208e5b128530b10c57804c7dbb2f53769ebf98ffa07ec1792feca939be56afa2d3c507f3548896b5c544a36b3f001543e006128d8660f2c0ddbc1449c8abb1df3b43d20f1a5025b9d702735f76085ed8438f7206db012caeaed7393e2f38baca305a21adf1627f34baf1c30f080b6dee62259851650721e019d7343938a92790bdd6254529c0f808458a9faeb22fd7647ee7f88ac42223461145a13beacf792f2c1732413522a19cd8912235b20392ab99fdfcbff0730a4f09266760360248046179586ffc6aac16fee4959ed42fcb2a42eae6708f5321b81cd2a6eba65b4e4bda95d492d29eb4cc91a973b94604aaeef27e8d4380fa4348bfad2a5d04ff0c8741a4c6580e70b68e2ba9e109be048200f2f679401340331225721cb55ae25bc76cee9a2e91ec0fbdc7c52121d619e6e8b6cd9b22dfd385235c5fa74641630665df948bbd625b2392b6ddcae6510e57f9b5571e434d164d58add1191e991fefa22ef88ddb9f5489bbd2384d72ca237a23f5763adca7ab06789342a05c3a6ab31726d9af0bfe9fdcd54c3018e8292f74f83314abc69e0c017972849c43a7befc13a798510bcd209b693392e6c55a28ee660e838515626c4e2508b5f957fe493efeabd2fb47a5d78d933e77f1d36a22d4c6b8aab3c7fb36e74d09e8ba6c523b1d700b66ce264c5b282967c63865e52320f520400b50d0ef2a08222620180d77baee87a88f3277f9f07521a15526a40ffe656543e75472c2ed85b40079ea7f9f5a256305c80af89e1bb4f01c8268d02f7ef74c73700ed2635aad1020b702196334d729bdd030e68a4d8f5a34c32ddd246078d8a5730262a24d821ee291398b6acdadee7435f04c5df0c63e76cae163194fce3f1bc643d6b2fb4343c5690da8dd0b5ed100622e81565ecdc4c087dbce2b40d61120db9c0a71d36d0b2fc435f1da021f09df04f8d36d769bb321ed0cf31e43aed2037954298ef427efa3510b0c3c206caf6547ebe2df41480c737356b706e24fcfa855fa8271a3d0c199b3ce7537b1dc19b9e187bf9751bd1d451ef42c088940be2dc8ac5d8593360a22dc754eb06ebf3c18c15976f7a370bd14131f01127253cc2a46aac7bf0060a1ec47d431a6328fa2954d56da83cee9f06e4668ec07be8bb72ad9814f1be714d239172334b189076d76c73a58eaa5184b686ebe8101a8c697fcf172d2fc00e5649329913813eabae5ecefa0adb5a8dc104ab1618c050924147e8813b32d6b2801294e148358c514b2b833f5e3967fc39e887532c43015a15850353650f0a78495124ffaec054c8adc76dd625e75c044f71200e0d713017ad21288895f478722e00ac38d4996d0a8f7fe171a795d19746ec017ebe31650700f4c76eedf63c9da0a8dfdd4b27fa8a699c93d2a448f7d06d9dc52c4930d050ecffce1b87fe2223283f247434ba2ebf193c7ed32333df5fadcde96619c2039c8f6e34b099aca91b8fa88c3f290d6dd09f106142bedf0ac29c1f8c056d22a6ce85f45df8c5ad17640a9f790bdf9bdd55324c366fd72f6d75ba9bfb0d1be288316eda51c89921428ce04183c87bb0a9244fa80f44f03b973351ad38ab3bd24e709d2c7de57471d47ac0df964339fa5a72efc3da90692468046f3f24ea0f705e2c59642e41084ce3b47ee6790d2b6bd4375231bcf180a0fcae273dfa23ade15de9325fa0b39a74136e0cffba7d78cb5e0bbf463b8e089e5e62de7f99e0dd31291daf49a2a338755fc5f0a1ce1c3a9f1002bae3b98cd20aeb5a8de38b42f412e05e532caec3a035c15f9ea3f8db06561ec62ce0c7c0c12422dea24b92134820cfa206a98dd13a1b0fcb0cea8e22e20a3f3c33ac959d9df686c6b551bb4b10211e79312775da1da6a4b2f100bd9ab53fbefaf3ae09315e7a73f7e1ae3e0090b0fa21c0e42a1df058480618724085e343a58da5e825852cabc240c940f14fddc0e99cac75b8d9a5c6758b55d6e4f9bac10532ebc34d243c09d684e8277b99cb32c1a2d59cdfcca7fcee82b75433c2bf0df101951bb97cb6d631528d1497d3777029f3dc8926abb3302b92c0a8f04eb79b16f945dd114b85fd7a944bab027454b1040c01850b5af972a3a996295ef6d617d4fba03a75390f548f465adca35d0362ad013e1cc6760e9d018c26eb2b9f9c0d6a8bb18bcff4407ebe1b1835b17fc822048485770506df4d49cf421671a025f2e3db015310776b35a82fd2f586b21bf2860ed1373a2eb05749addd1898d4bcde45aaa34d4c94dc73a44a9f5266b9e311864709503a3e58518ce96a2c3e8dfc1e447609ee2cf76b6ca203a495c2b0125151c01ce091cd9e331aea74be7bad8fe0138c94b033e12da7c2c19cc8ec55c99091fb9d4006bf121c31dede198a0fdb9aa08420fb9f46eac4c803db032ec7c7318d16a34197350ede6c0343e321d3e860afc6fcd66ced27d0c13142518f054761d884aed3762b43ecd50a59df25c236e708789b8378409951cf05a14ae2f8c301e86e10e9a2c75dac1a808064a70a783f1bd6bcd5d1762e588332d3db71ad77d2c66895f66e4678a867b72d8fa053c51ebea627870628968d693351d2afffd301784ef0093bad30c1ba4ffdbf325c685fd021d468b962ef6e3834b1b16fc1a8026866eb630df467b433bc5030d03e31d1845fab0e2aad4e93678cae3b237e8751e0f33f6eeb7edfeecb02303f28ce4cb8fac0746f818f7b97f80284d2981894116758388812c470d1c522502beb11a2baa91bd7b9259977e6bb71cf538d0ba1f080cb65252c017f7d915cf72e1305cd9768c9cbc233d7a2cbac65601dd702b9b13f84d42fad54b4918a79b6f9b87317a1c84d61bdc347d2b2a707baa44e4727e00fd30dc95e3ffb7627e96bc745a782d686a6590d708e6b424ad1c38d32d78030685bf5d3407264c14f0f86a8db1bfd48a0ef5cc30565ec4af80f653686ebae92ccdc6a6e56fb0ecb116fe6bdb5e9b5df8def29e4d13ba2b4e1c43df8ac1ea591f708744d587ec77aa0f74434a6c0775234b42d67a303f914b2488df06d3adfb21dde903a21536b62058f8fa4e09577da8dced3d6a86f8c9493b47afb9e521bc2e0377854af2ae69bb1af04d10a8faabdeb54259dcbda8e554725b9f33a07c432a3e24bb51b780207643253c9de986abfbae1d89d751584a90996100e543d1482ce8cf358632f600945281f97aa741f687462fa3a23c1c01cc686030b029ce280ca0d82cea2b1c47193e344a6e4da2f3a256652bc22ed410f68ce3dff1894f7a1ffdbef96a47fa72bd8f73a38a915c2b099e0b4ece88a14bc60433cb8e405b050b234ee3a351f3635b76310c77f8886d3281c1b7a50f3626a2cc0ea25994d76d00d28261d65c0843cdd25b5f51f0f4e5c522c4aa467a5776b22d942e3e1de4601229057f6cbc81db25945b80c9c7762a9417c5e27965c09a3721087a8831cbe90fb7d1586f76ea5e296da6406828b0fb40b3322182ac14691b31ec674b650f030ed585df07aca077101dbf75aeb7d8832888831d9362571d9ddd452025455cf92707127b8374b4f589739517b47f44ed47c06f09bfa08e7d71e37cbbb95c9648300cbe5dc0454b493fc02b0e2babf80ac9ae3db7494f0cf7dc17e1e8ad12586001f75c172eac0f81ab8c4c2a69694dc9ca9442ee08444baf13bc42d3a8a9f1ed258b34cec704010c9cf981c7788d281f40d8992b1a2fd99509346da4e18068df280916a9553dd08cf6afac441b574aa35b74fc70521feb1044e4ff0651536c0f081ef95bf341fff600916e297ca6f383dbab078e848ac0dfdcf5a8857477995105d766a9d21236c4b06ba90d461862696361346329ca90b3a5e4fb045e6e0853277376295c715272fe5a774c7282a90b75068993786cd6fb6f11f671f10502ad01876b03a95d256fbe9d0b2ca9458e061b115c572c4beb67cf15bd0db54a1b9f2998435d14c36ed021240accc567f42705d1d96841da9e4aa166b30be259771a1eaea35bdac859a94e677251349fae7455a5df632c235b0d25e53e019da6ae7100171dab09430d4db4986c2716a8626194be4b67df0d4070e6a007acde0809f9012832959dad3f231e74c2dd4d3197830da3a0e2c48073e5cca2d41b5af904dd19078c02e4f2b4a2d0ab7f080b92fb061fd1cda443d33e9761d04b49b976141015c3d2e7c4e94871a724b8f24df4d4ab2688b3a0615bce95bcc6e919b1919ff3", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba010000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index 38cac38db..40932f1ee 100644 --- a/circuits/benchmarks/results_insecure/report.md +++ b/circuits/benchmarks/results_insecure/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-21 08:52:11 UTC +**Generated:** 2026-05-21 10:09:51 UTC -**Git Branch:** `feat/1525` -**Git Commit:** `a6455239f48858b46d3a55562def9147c130c18d` +**Git Branch:** `feat/1549` +**Git Commit:** `1934c17dbe9cf5344fa0cb9bf247f5721a6d2f70` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -15,36 +15,36 @@ | Circuit | Constraints | Prove time (s) | Verify time (ms) | Proof size (KB) | | -------------------- | ----------- | -------------- | ---------------- | --------------- | -| C0 | 6847 | 0.13 | 25.55 | 15.88 | -| C1 | 57818 | 0.35 | 26.34 | 15.88 | -| C2a | 142625 | 0.82 | 25.47 | 15.88 | -| C2b | 198355 | 0.91 | 26.32 | 15.88 | -| C3a | 132633 | 0.90 | 27.00 | 15.88 | -| C3b | 132633 | 0.90 | 27.00 | 15.88 | -| C4a | 92515 | 0.52 | 26.04 | 15.88 | -| C4b | 92515 | 0.52 | 26.04 | 15.88 | -| C5 | 151717 | 0.80 | 25.86 | 15.88 | -| user_data_encryption | 53732 | 0.33 | 34.30 | 15.88 | -| C6 | 86927 | 0.52 | 26.58 | 15.88 | -| C7 | 104273 | 0.56 | 28.38 | 15.88 | +| C0 | 6847 | 0.12 | 25.67 | 15.88 | +| C1 | 57818 | 0.34 | 25.70 | 15.88 | +| C2a | 142625 | 0.78 | 26.08 | 15.88 | +| C2b | 198355 | 0.86 | 26.92 | 15.88 | +| C3a | 132633 | 0.80 | 26.97 | 15.88 | +| C3b | 132633 | 0.80 | 26.97 | 15.88 | +| C4a | 92515 | 0.50 | 26.99 | 15.88 | +| C4b | 92515 | 0.50 | 26.99 | 15.88 | +| C5 | 151717 | 0.81 | 28.04 | 15.88 | +| user_data_encryption | 53732 | 0.34 | 26.75 | 15.88 | +| C6 | 86927 | 0.52 | 26.36 | 15.88 | +| C7 | 104273 | 0.50 | 26.75 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042430 | 176112 | 3218542 | -| Π_user | 15.88 KB | 0.12 KB | 2972893 | 170308 | 3143201 | -| Π_dec | 10.69 KB | 3.47 KB | 3553544 | 187152 | 3740696 | +| Π_DKG | 10.69 KB | 0.47 KB | 3042552 | 176232 | 3218784 | +| Π_user | 15.88 KB | 0.12 KB | 2973037 | 170308 | 3143345 | +| Π_dec | 10.69 KB | 3.47 KB | 3553605 | 187212 | 3740817 | ### Role / Phase / Activity | Role | Phase | Activity | Prove time | Proof size | Bandwidth | | --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 304.14 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | combine folds + C5 | 0.80 s | 10.69 KB | 11.16 KB | +| Each ciphernode | P1 | one-time DKG participation | 305.20 s | 127.00 KB | 128.19 KB | +| Aggregator | P2 | combine folds + C5 | 0.81 s | 10.69 KB | 11.16 KB | | User | P3 | per user input | 0.66 s | 15.88 KB | 16.00 KB | | Each ciphernode | P4 | per computation output (C6) | 0.52 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 79.88 s | 10.69 KB | 14.16 KB | +| Aggregator | P4 | per computation output (C7+fold) | 79.92 s | 10.69 KB | 14.16 KB | ## Integration test (`test_trbfv_actor`) @@ -53,15 +53,15 @@ | Phase | Duration (s) | | ------------------------------------------- | ------------ | | Starting trbfv actor test | 0.00 | -| Setup completed | 3.07 | -| Committee Setup Completed | 20.23 | +| Setup completed | 3.05 | +| Committee Setup Completed | 20.25 | | Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 304.14 | -| E3Request -> PublicKeyAggregated | 306.70 | +| ThresholdShares -> PublicKeyAggregated | 305.20 | +| E3Request -> PublicKeyAggregated | 307.80 | | Application CT Gen | 0.31 | | Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 79.88 | -| Entire Test | 410.21 | +| Ciphertext published -> PlaintextAggregated | 79.92 | +| Entire Test | 411.34 | ### Thread pool (same process as integration test) @@ -75,26 +75,26 @@ | Name | Avg (s) | Runs | Total (s) | | ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.11 | 3 | 0.33 | -| CalculateDecryptionShare | 0.61 | 3 | 1.83 | +| CalculateDecryptionKey | 0.11 | 3 | 0.34 | +| CalculateDecryptionShare | 0.61 | 3 | 1.82 | | CalculateThresholdDecryption | 0.56 | 1 | 0.56 | | GenEsiSss | 0.12 | 3 | 0.37 | | GenPkShareAndSkSss | 0.23 | 3 | 0.68 | -| ZkDecryptedSharesAggregation | 8.50 | 1 | 8.50 | -| ZkDecryptionAggregation | 49.37 | 1 | 49.37 | -| ZkDkgAggregation | 21.12 | 1 | 21.12 | -| ZkDkgShareDecryption | 1.47 | 6 | 8.80 | -| ZkNodeDkgFold | 62.33 | 3 | 186.98 | -| ZkPkAggregation | 2.20 | 1 | 2.20 | -| ZkPkBfv | 0.34 | 3 | 1.01 | -| ZkPkGeneration | 1.35 | 3 | 4.05 | -| ZkShareComputation | 2.68 | 6 | 16.09 | -| ZkShareEncryption | 2.51 | 24 | 60.15 | -| ZkThresholdShareDecryption | 6.18 | 3 | 18.53 | -| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.30 | -| ZkVerifyShareProofs | 0.22 | 5 | 1.11 | - -Sum of tracked operation wall time: **381.99 s** (often much larger than end-to-end wall clock +| ZkDecryptedSharesAggregation | 8.43 | 1 | 8.43 | +| ZkDecryptionAggregation | 49.59 | 1 | 49.59 | +| ZkDkgAggregation | 20.54 | 1 | 20.54 | +| ZkDkgShareDecryption | 1.49 | 6 | 8.94 | +| ZkNodeDkgFold | 62.38 | 3 | 187.14 | +| ZkPkAggregation | 2.13 | 1 | 2.13 | +| ZkPkBfv | 0.34 | 3 | 1.02 | +| ZkPkGeneration | 1.38 | 3 | 4.14 | +| ZkShareComputation | 2.76 | 6 | 16.54 | +| ZkShareEncryption | 2.55 | 24 | 61.13 | +| ZkThresholdShareDecryption | 6.14 | 3 | 18.42 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.29 | +| ZkVerifyShareProofs | 0.23 | 5 | 1.14 | + +Sum of tracked operation wall time: **383.24 s** (often much larger than end-to-end wall clock because work runs in parallel). ## Raw circuit benchmark JSON (Nargo) diff --git a/crates/aggregator/Cargo.toml b/crates/aggregator/Cargo.toml index 078dc82cc..4204aee14 100644 --- a/crates/aggregator/Cargo.toml +++ b/crates/aggregator/Cargo.toml @@ -28,6 +28,7 @@ e3-request = { workspace = true } e3-sortition = { workspace = true } e3-zk-helpers = { workspace = true } e3-utils = { workspace = true } +e3-zk-prover = { workspace = true } futures = { workspace = true } serde = { workspace = true } tracing = { workspace = true } diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index 4f9324572..41a027494 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -6,7 +6,8 @@ use crate::committee::committee_addresses_from_nodes; use actix::prelude::*; -use anyhow::Result; +use alloy::primitives::Address; +use anyhow::{anyhow, bail, ensure, Context as _, Result}; use e3_data::Persistable; use e3_events::{ prelude::*, BusHandle, CircuitName, ComputeRequest, ComputeRequestError, ComputeResponse, @@ -22,6 +23,7 @@ use e3_fhe::{Fhe, GetAggregatePublicKey}; use e3_fhe_params::BfvPreset; use e3_utils::NotifySync; use e3_utils::{ArcBytes, MAILBOX_LIMIT}; +use e3_zk_prover::extract_node_fold_agg_commits; use std::collections::{BTreeSet, HashMap}; use std::sync::Arc; use tracing::{error, info, warn}; @@ -30,6 +32,42 @@ use tracing::{error, info, warn}; /// Must stay in lock-step with the Noir circuit's output ABI declaration. const C5_PK_COMMITMENT_FIELD: &str = "commitment"; +fn verify_dkg_fold_attestation( + e3_id: &E3id, + party_id: u64, + proof: &Proof, + attestation: &e3_events::SignedDkgFoldAttestation, + expected_node: &str, + committee_n: usize, + committee_h: usize, + n_moduli: usize, +) -> Result<()> { + ensure!( + attestation.payload.e3_id == *e3_id, + "attestation e3_id mismatch" + ); + ensure!( + attestation.payload.party_id == party_id, + "attestation party_id mismatch" + ); + let expected: Address = expected_node + .parse() + .with_context(|| format!("invalid committee node address {expected_node}"))?; + ensure!( + attestation.verify_signer(&expected)?, + "fold attestation signer does not match committee node for party {party_id}" + ); + let (extracted_party, commits) = + extract_node_fold_agg_commits(proof, committee_n, committee_h, n_moduli) + .map_err(|e| anyhow!("{e}"))?; + ensure!(extracted_party == party_id, "NodeFold party_id mismatch"); + ensure!( + commits == attestation.payload.agg_commits, + "NodeFold commits do not match signed attestation" + ); + Ok(()) +} + /// Extract the hash-based aggregated PK commitment from the signed C5 proof. /// This is the last public signal of `CircuitName::PkAggregation`. fn extract_pk_commitment(c5_proof: &Proof) -> Result<[u8; 32]> { @@ -78,6 +116,8 @@ pub enum PublicKeyAggregatorState { public_key: ArcBytes, keyshare_bytes: Vec, nodes: OrderedSet, + /// Registered node address per sortition `party_id` (for fold attestation checks). + party_nodes: HashMap, /// DKG recursive proofs per party (restart-critical). dkg_node_proofs: HashMap>, honest_party_ids: BTreeSet, @@ -466,11 +506,17 @@ impl PublicKeyAggregator { ec.clone(), )?; + let party_nodes: HashMap = honest_entries + .iter() + .map(|(pid, node, _, _)| (*pid, node.clone())) + .collect(); + self.state.try_mutate(&ec, |_| { Ok(PublicKeyAggregatorState::GeneratingC5Proof { public_key: pubkey.clone(), keyshare_bytes, nodes: honest_nodes_set, + party_nodes, dkg_node_proofs: HashMap::new(), honest_party_ids: honest_party_ids.clone(), dishonest_parties: dishonest_parties.clone(), @@ -519,6 +565,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -533,6 +580,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -559,7 +607,10 @@ impl PublicKeyAggregator { let state = self.state.get(); let Some(PublicKeyAggregatorState::GeneratingC5Proof { - dkg_node_proofs, .. + party_nodes, + dkg_node_proofs, + honest_party_ids, + .. }) = state.as_ref() else { info!( @@ -577,6 +628,51 @@ impl PublicKeyAggregator { return Ok(()); } + if honest_party_ids.contains(&msg.party_id) { + let Some(expected_node) = party_nodes.get(&msg.party_id) else { + warn!( + party_id = msg.party_id, + "DKG fold from party without registered node address — rejecting" + ); + return Ok(()); + }; + let Some(ref proof) = msg.aggregated_proof else { + warn!( + party_id = msg.party_id, + "honest party reported DKG fold without proof — rejecting" + ); + return Ok(()); + }; + let Some(ref attestation) = msg.fold_attestation else { + warn!( + party_id = msg.party_id, + "DKG fold missing SignedDkgFoldAttestation — rejecting (attribution)" + ); + return Ok(()); + }; + let meta = self.params_preset.metadata(); + let committee_n = party_nodes.len(); + let committee_h = committee_n; + let n_moduli = meta.num_moduli as usize; + if let Err(e) = verify_dkg_fold_attestation( + &self.e3_id, + msg.party_id, + proof, + attestation, + expected_node, + committee_n, + committee_h, + n_moduli, + ) { + warn!( + party_id = msg.party_id, + error = %e, + "DKG fold attestation verification failed — rejecting" + ); + return Ok(()); + } + } + info!( "PublicKeyAggregator: buffered DKG proof from party {} (buffered={})", msg.party_id, @@ -588,6 +684,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, mut dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -604,6 +701,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -680,6 +778,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -696,6 +795,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -737,7 +837,7 @@ impl PublicKeyAggregator { return Ok(()); } - let committee_addresses = committee_addresses_from_nodes(nodes)?; + let committee_addresses = committee_addresses_from_nodes(&nodes)?; #[cfg(debug_assertions)] { let n_registered = committee_addresses.len(); @@ -769,6 +869,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -784,6 +885,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -908,6 +1010,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -924,6 +1027,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -937,6 +1041,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -989,6 +1094,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -1005,6 +1111,7 @@ impl PublicKeyAggregator { public_key, keyshare_bytes, nodes, + party_nodes, dkg_node_proofs, honest_party_ids, dishonest_parties, @@ -1321,6 +1428,7 @@ mod tests { public_key: ArcBytes::from_bytes(&[1, 2, 3]), keyshare_bytes: Vec::new(), nodes: OrderedSet::new(), + party_nodes: HashMap::new(), dkg_node_proofs: HashMap::new(), honest_party_ids: BTreeSet::new(), dishonest_parties: BTreeSet::new(), @@ -1465,4 +1573,48 @@ mod tests { Ok(()) } + + #[actix::test] + async fn honest_dkg_fold_without_attestation_is_not_buffered() -> Result<()> { + let correlation_id = CorrelationId::new(); + let mut initial_state = generating_c5_state(correlation_id); + let PublicKeyAggregatorState::GeneratingC5Proof { + ref mut party_nodes, + ref mut honest_party_ids, + .. + } = initial_state + else { + unreachable!(); + }; + honest_party_ids.insert(2); + party_nodes.insert(2, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_string()); + + let (mut aggregator, _history, e3_id) = build_public_key_aggregator(initial_state).await?; + let ec = test_ctx(DKGRecursiveAggregationComplete { + e3_id: e3_id.clone(), + party_id: 2, + aggregated_proof: Some(dummy_proof(CircuitName::NodeFold)), + fold_attestation: None, + }); + + aggregator.handle_dkg_recursive_aggregation_complete(TypedEvent::new( + DKGRecursiveAggregationComplete { + e3_id: e3_id.clone(), + party_id: 2, + aggregated_proof: Some(dummy_proof(CircuitName::NodeFold)), + fold_attestation: None, + }, + ec, + ))?; + + let Some(PublicKeyAggregatorState::GeneratingC5Proof { + dkg_node_proofs, .. + }) = aggregator.state.get() + else { + panic!("expected GeneratingC5Proof state"); + }; + assert!(!dkg_node_proofs.contains_key(&2)); + + Ok(()) + } } diff --git a/crates/events/src/enclave_event/dkg_fold_attestation.rs b/crates/events/src/enclave_event/dkg_fold_attestation.rs new file mode 100644 index 000000000..9fcaf3f6f --- /dev/null +++ b/crates/events/src/enclave_event/dkg_fold_attestation.rs @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! ECDSA attestation binding a node's [`CircuitName::NodeFold`] output to its committee address. +//! +//! Used to close the attribution gap between `party_ids[i]` in the DKG aggregator and the +//! operator that produced fold row `i`: the aggregator cannot permute folds without valid +//! signatures from each claimed party. + +use crate::E3id; +use alloy::primitives::{keccak256, Address, U256}; +use alloy::signers::{local::PrivateKeySigner, SignerSync}; +use alloy::sol_types::SolValue; +use anyhow::{anyhow, Result}; +use derivative::Derivative; +use e3_utils::utility_types::ArcBytes; +use serde::{Deserialize, Serialize}; + +/// Commitments surfaced from a [`CircuitName::NodeFold`] proof (C4 aggregate outputs). +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DkgFoldAggCommits { + pub sk_agg_commit: [u8; 32], + pub esm_agg_commit: [u8; 32], +} + +/// Payload signed after `NodeDkgFold` completes. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct DkgFoldAttestationPayload { + pub e3_id: E3id, + /// Sortition / committee slot id (index into on-chain `topNodes` when ids are dense). + pub party_id: u64, + pub agg_commits: DkgFoldAggCommits, +} + +impl DkgFoldAttestationPayload { + /// Must match `DKG_FOLD_ATTESTATION_TYPEHASH` in `CiphernodeRegistryOwnable.sol` when added. + pub fn typehash() -> [u8; 32] { + keccak256( + "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", + ) + .into() + } + + pub fn digest(&self) -> Result<[u8; 32]> { + let e3_id_u256: U256 = self + .e3_id + .clone() + .try_into() + .map_err(|_| anyhow!("E3id cannot be converted to U256"))?; + let encoded = ( + Self::typehash(), + U256::from(self.e3_id.chain_id()), + e3_id_u256, + U256::from(self.party_id), + self.agg_commits.sk_agg_commit, + self.agg_commits.esm_agg_commit, + ) + .abi_encode(); + Ok(keccak256(&encoded).into()) + } +} + +/// `eth_sign` over [`DkgFoldAttestationPayload::digest`]. +#[derive(Derivative, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derivative(Debug)] +pub struct SignedDkgFoldAttestation { + pub payload: DkgFoldAttestationPayload, + #[derivative(Debug(format_with = "e3_utils::formatters::hexf"))] + pub signature: ArcBytes, +} + +impl SignedDkgFoldAttestation { + pub fn sign(payload: DkgFoldAttestationPayload, signer: &PrivateKeySigner) -> Result { + let digest = payload.digest()?; + let sig = signer + .sign_message_sync(&digest) + .map_err(|e| anyhow!("Failed to sign DkgFoldAttestation: {e}"))?; + Ok(Self { + payload, + signature: ArcBytes::from_bytes(&sig.as_bytes()), + }) + } + + pub fn recover_address(&self) -> Result
{ + use alloy::primitives::Signature; + let sig = Signature::try_from(&self.signature[..]) + .map_err(|e| anyhow!("Invalid DkgFoldAttestation signature: {e}"))?; + let digest = self.payload.digest()?; + sig.recover_address_from_msg(&digest) + .map_err(|e| anyhow!("Failed to recover DkgFoldAttestation signer: {e}")) + } + + pub fn verify_signer(&self, expected: &Address) -> Result { + Ok(self.recover_address()? == *expected) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy::signers::local::PrivateKeySigner; + + #[test] + fn sign_and_recover_roundtrip() { + let signer: PrivateKeySigner = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + .parse() + .unwrap(); + let payload = DkgFoldAttestationPayload { + e3_id: E3id::new("0", 1), + party_id: 1, + agg_commits: DkgFoldAggCommits { + sk_agg_commit: [7u8; 32], + esm_agg_commit: [9u8; 32], + }, + }; + let signed = SignedDkgFoldAttestation::sign(payload, &signer).unwrap(); + assert_eq!(signed.recover_address().unwrap(), signer.address()); + } +} diff --git a/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs b/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs index 0458aabdb..ddcd1220b 100644 --- a/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs +++ b/crates/events/src/enclave_event/dkg_recursive_aggregation_complete.rs @@ -10,7 +10,7 @@ //! [`PublicKeyAggregator`] collects these from all honest nodes for the //! cross-node aggregation phase. -use crate::{E3id, Proof}; +use crate::{E3id, Proof, SignedDkgFoldAttestation}; use serde::{Deserialize, Serialize}; /// NodeProofAggregator -> PublicKeyAggregator: fully aggregated DKG node proof. @@ -20,4 +20,23 @@ pub struct DKGRecursiveAggregationComplete { pub e3_id: E3id, pub party_id: u64, pub aggregated_proof: Option, + /// Binds the fold to the operator's registered address via `sk_agg` / `esm_agg` commits. + #[serde(default)] + pub fold_attestation: Option, +} + +impl DKGRecursiveAggregationComplete { + pub fn with_attestation( + e3_id: E3id, + party_id: u64, + aggregated_proof: Option, + fold_attestation: Option, + ) -> Self { + Self { + e3_id, + party_id, + aggregated_proof, + fold_attestation, + } + } } diff --git a/crates/events/src/enclave_event/mod.rs b/crates/events/src/enclave_event/mod.rs index 065517d96..5437e9812 100644 --- a/crates/events/src/enclave_event/mod.rs +++ b/crates/events/src/enclave_event/mod.rs @@ -25,6 +25,7 @@ mod decryption_share_proof_signed; mod decryption_share_proofs; mod decryptionshare_created; mod die; +mod dkg_fold_attestation; mod dkg_inner_proof_ready; mod dkg_recursive_aggregation_complete; mod e3_failed; @@ -91,6 +92,7 @@ pub use decryption_share_proof_signed::*; pub use decryption_share_proofs::*; pub use decryptionshare_created::*; pub use die::*; +pub use dkg_fold_attestation::*; pub use dkg_inner_proof_ready::*; pub use dkg_recursive_aggregation_complete::*; pub use e3_failed::*; diff --git a/crates/evm/src/slashing_manager_sol_writer.rs b/crates/evm/src/slashing_manager_sol_writer.rs index adf1ea3e8..634f2f974 100644 --- a/crates/evm/src/slashing_manager_sol_writer.rs +++ b/crates/evm/src/slashing_manager_sol_writer.rs @@ -209,7 +209,7 @@ fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Vec { .map(|v| Bytes::from(v.signature.extract_bytes())) .collect(); - (proof_type, voters, agrees, data_hashes, signatures).abi_encode() + (proof_type, voters, agrees, data_hashes, signatures).abi_encode_params() } async fn submit_slash_proposal( diff --git a/crates/zk-prover/scripts/build_fixtures.sh b/crates/zk-prover/scripts/build_fixtures.sh index c7d17a3d6..466eca693 100644 --- a/crates/zk-prover/scripts/build_fixtures.sh +++ b/crates/zk-prover/scripts/build_fixtures.sh @@ -22,3 +22,11 @@ fi echo "Building circuits..." pnpm install && pnpm build:circuits + +# Keep integration-test fixture in sync when the dummy circuit is built. +dummy_artifact="./circuits/bin/dummy/dummy.json" +fixture="./crates/zk-prover/tests/fixtures/dummy.json" +if [ -f "$dummy_artifact" ]; then + mkdir -p "$(dirname "$fixture")" + cp "$dummy_artifact" "$fixture" +fi diff --git a/crates/zk-prover/src/actors/mod.rs b/crates/zk-prover/src/actors/mod.rs index 66e146185..914f0b17b 100644 --- a/crates/zk-prover/src/actors/mod.rs +++ b/crates/zk-prover/src/actors/mod.rs @@ -69,10 +69,10 @@ pub fn setup_zk_actors(bus: &BusHandle, backend: &ZkBackend, signer: PrivateKeyS let zk_actor = ZkActor::new(backend).start(); let verifier = zk_actor.clone().recipient(); - let proof_request = ProofRequestActor::setup(bus, signer); + let proof_request = ProofRequestActor::setup(bus, signer.clone()); let proof_verification = ProofVerificationActor::setup(bus, verifier); let share_verification = ShareVerificationActor::setup(bus); - let node_proof_aggregator = NodeProofAggregator::setup(bus); + let node_proof_aggregator = NodeProofAggregator::setup(bus, signer); ZkActors { zk_actor, diff --git a/crates/zk-prover/src/actors/node_proof_aggregator.rs b/crates/zk-prover/src/actors/node_proof_aggregator.rs index 8fed4f778..03092e98d 100644 --- a/crates/zk-prover/src/actors/node_proof_aggregator.rs +++ b/crates/zk-prover/src/actors/node_proof_aggregator.rs @@ -10,16 +10,19 @@ use std::collections::{BTreeMap, HashMap}; use actix::{Actor, Addr, Context, Handler}; +use alloy::signers::local::PrivateKeySigner; use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeResponse, ComputeResponseKind, - CorrelationId, DKGInnerProofReady, DKGRecursiveAggregationComplete, E3id, EnclaveEvent, - EnclaveEventData, EventContext, EventPublisher, EventSubscriber, EventType, NodeDkgFoldRequest, - Proof, Sequenced, ShareEncryptionProofRequest, ThresholdSharePending, TypedEvent, ZkRequest, - ZkResponse, + CorrelationId, DKGInnerProofReady, DKGRecursiveAggregationComplete, DkgFoldAttestationPayload, + E3id, EnclaveEvent, EnclaveEventData, EventContext, EventPublisher, EventSubscriber, EventType, + NodeDkgFoldRequest, Proof, Sequenced, ShareEncryptionProofRequest, SignedDkgFoldAttestation, + ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse, }; use e3_fhe_params::build_pair_for_preset; use tracing::{error, info, warn}; +use crate::node_fold_public::extract_node_fold_agg_commits; + /// Metadata from [`ThresholdSharePending`] for slot indices and sizing. struct NodeDkgFoldMeta { party_id: u64, @@ -44,23 +47,25 @@ struct DkgProofCollectionState { /// Actor that collects DKG inner proofs and dispatches a single [`ZkRequest::NodeDkgFold`]. pub struct NodeProofAggregator { bus: BusHandle, + signer: PrivateKeySigner, states: HashMap, fold_correlation: HashMap, pending_inner_proofs: HashMap>, } impl NodeProofAggregator { - pub fn new(bus: &BusHandle) -> Self { + pub fn new(bus: &BusHandle, signer: PrivateKeySigner) -> Self { Self { bus: bus.clone(), + signer, states: HashMap::new(), fold_correlation: HashMap::new(), pending_inner_proofs: HashMap::new(), } } - pub fn setup(bus: &BusHandle) -> Addr { - let addr = Self::new(bus).start(); + pub fn setup(bus: &BusHandle, signer: PrivateKeySigner) -> Addr { + let addr = Self::new(bus, signer).start(); bus.subscribe(EventType::ThresholdSharePending, addr.clone().into()); bus.subscribe(EventType::DKGInnerProofReady, addr.clone().into()); bus.subscribe(EventType::ComputeResponse, addr.clone().into()); @@ -83,6 +88,7 @@ impl NodeProofAggregator { e3_id: e3_id.clone(), party_id: msg.full_share.party_id, aggregated_proof: None, + fold_attestation: None, }, ec, ) { @@ -304,16 +310,87 @@ impl NodeProofAggregator { return; }; + let party_id = state.meta.party_id; + let committee_n = state.meta.committee_n; + let committee_h = committee_n; + let n_moduli = state.meta.n_moduli; + + let fold_attestation = + match extract_node_fold_agg_commits(&proof, committee_n, committee_h, n_moduli) { + Ok((extracted_party, commits)) => { + if extracted_party != party_id { + error!( + e3_id = %e3_id, + expected_party_id = party_id, + extracted_party_id = extracted_party, + "NodeFold public party_id does not match sortition party_id" + ); + None + } else { + let payload = DkgFoldAttestationPayload { + e3_id: e3_id.clone(), + party_id, + agg_commits: commits, + }; + match SignedDkgFoldAttestation::sign(payload, &self.signer) { + Ok(signed) => Some(signed), + Err(e) => { + error!( + e3_id = %e3_id, + party_id, + error = %e, + "failed to sign DkgFoldAttestation" + ); + None + } + } + } + } + Err(e) => { + error!( + e3_id = %e3_id, + party_id, + error = %e, + "failed to extract sk_agg/esm_agg from NodeFold proof" + ); + None + } + }; + + if fold_attestation.is_none() { + error!( + e3_id = %e3_id, + party_id, + "NodeDkgFold succeeded but fold attestation missing — publishing without proof" + ); + if let Err(err) = self.bus.publish( + DKGRecursiveAggregationComplete { + e3_id: e3_id.clone(), + party_id, + aggregated_proof: None, + fold_attestation: None, + }, + state.last_ec, + ) { + error!( + "NodeProofAggregator: failed to publish DKGRecursiveAggregationComplete for E3 {}: {err}", + e3_id + ); + } + return; + } + info!( "NodeProofAggregator: NodeDkgFold complete for E3 {} party {} — publishing DKGRecursiveAggregationComplete", - e3_id, state.meta.party_id + e3_id, party_id ); if let Err(err) = self.bus.publish( DKGRecursiveAggregationComplete { e3_id: e3_id.clone(), - party_id: state.meta.party_id, + party_id, aggregated_proof: Some(proof), + fold_attestation, }, state.last_ec, ) { @@ -428,6 +505,7 @@ impl NodeProofAggregator { e3_id: e3_id.clone(), party_id: state.meta.party_id, aggregated_proof: None, + fold_attestation: None, }, ec, ) { @@ -455,6 +533,12 @@ mod tests { EventContext::::from(data.into()).sequence(0) } + fn test_signer() -> PrivateKeySigner { + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + .parse() + .expect("test signer") + } + fn dummy_proof(seed: u8) -> Proof { Proof::new( CircuitName::PkAggregation, @@ -472,7 +556,7 @@ mod tests { #[actix::test] async fn node_dkg_fold_compute_error_emits_none_aggregation_result() -> Result<()> { let (bus, _rng, _seed, _params, _crp, _errors, history) = get_common_setup(None)?; - let mut aggregator = NodeProofAggregator::new(&bus); + let mut aggregator = NodeProofAggregator::new(&bus, test_signer()); let e3_id = E3id::new("42", 1); let correlation_id = CorrelationId::new(); @@ -496,6 +580,7 @@ mod tests { e3_id: e3_id.clone(), party_id: 7, aggregated_proof: None, + fold_attestation: None, }), }, ); @@ -532,6 +617,7 @@ mod tests { e3_id: e3_id.clone(), party_id: 7, aggregated_proof: None, + fold_attestation: None, }), )); @@ -552,7 +638,7 @@ mod tests { #[actix::test] async fn early_inner_proof_is_prebuffered_until_collection_starts() -> Result<()> { let (bus, _rng, _seed, _params, _crp, _errors, history) = get_common_setup(None)?; - let mut aggregator = NodeProofAggregator::new(&bus); + let mut aggregator = NodeProofAggregator::new(&bus, test_signer()); let e3_id = E3id::new("43", 1); let early_proof = dummy_proof(10); @@ -596,6 +682,7 @@ mod tests { e3_id: e3_id.clone(), party_id: 7, aggregated_proof: None, + fold_attestation: None, }), ); diff --git a/crates/zk-prover/src/lib.rs b/crates/zk-prover/src/lib.rs index 98ee6344a..4d8707646 100644 --- a/crates/zk-prover/src/lib.rs +++ b/crates/zk-prover/src/lib.rs @@ -9,6 +9,7 @@ mod backend; mod circuits; mod config; mod error; +mod node_fold_public; mod prover; pub mod test_utils; mod traits; @@ -32,6 +33,7 @@ pub use config::{verify_checksum, BbTarget, CircuitInfo, VersionInfo, ZkConfig}; pub use e3_events::CircuitVariant; pub use e3_zk_helpers::circuits::dkg::pk::circuit::PkCircuit; pub use error::ZkError; +pub use node_fold_public::extract_node_fold_agg_commits; pub use prover::ZkProver; pub use traits::Provable; pub use witness::{input_map, CompiledCircuit, WitnessGenerator}; diff --git a/crates/zk-prover/src/node_fold_public.rs b/crates/zk-prover/src/node_fold_public.rs new file mode 100644 index 000000000..f94e62ff8 --- /dev/null +++ b/crates/zk-prover/src/node_fold_public.rs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Public IO layout for [`CircuitName::NodeFold`] (must stay aligned with `node_fold/src/main.nr`). + +use crate::circuits::utils::bytes_to_field_strings; +use crate::error::ZkError; +use e3_events::{CircuitName, DkgFoldAggCommits, Proof}; + +/// Total public field count for `node_fold` at committee size `n`, honest `h`, threshold moduli `l`. +pub fn node_fold_public_field_count(n: usize, h: usize, l: usize) -> usize { + 11 + n + 2 * (n + h) * l +} + +fn field_hex_to_bytes32(field: &str) -> Result<[u8; 32], ZkError> { + let s = field.strip_prefix("0x").unwrap_or(field); + if s.len() > 64 { + return Err(ZkError::InvalidInput(format!( + "field hex too long for bytes32: {field}" + ))); + } + let mut out = [0u8; 32]; + let decoded = hex::decode(s).map_err(|e| ZkError::InvalidInput(e.to_string()))?; + let start = 32usize.saturating_sub(decoded.len()); + out[start..].copy_from_slice(&decoded); + Ok(out) +} + +fn field_hex_to_u64(field: &str) -> Result { + let s = field.strip_prefix("0x").unwrap_or(field); + let trimmed = s.trim_start_matches('0'); + let trimmed = if trimmed.is_empty() { "0" } else { trimmed }; + u64::from_str_radix(trimmed, 16).map_err(|e| ZkError::InvalidInput(e.to_string())) +} + +/// Read `party_id`, `sk_agg_commit`, and `esm_agg_commit` from a `NodeFold` proof. +pub fn extract_node_fold_agg_commits( + proof: &Proof, + committee_n: usize, + committee_h: usize, + n_moduli: usize, +) -> Result<(u64, DkgFoldAggCommits), ZkError> { + if proof.circuit != CircuitName::NodeFold { + return Err(ZkError::InvalidInput(format!( + "expected NodeFold proof, got {}", + proof.circuit + ))); + } + let fields = bytes_to_field_strings(proof.public_signals.as_ref())?; + let expected = node_fold_public_field_count(committee_n, committee_h, n_moduli); + if fields.len() != expected { + return Err(ZkError::InvalidInput(format!( + "NodeFold public field count {} != expected {} (n={committee_n}, h={committee_h}, l={n_moduli})", + fields.len(), + expected + ))); + } + let party_id = field_hex_to_u64(&fields[0])?; + let sk_agg_commit = field_hex_to_bytes32(&fields[fields.len() - 2])?; + let esm_agg_commit = field_hex_to_bytes32(&fields[fields.len() - 1])?; + Ok(( + party_id, + DkgFoldAggCommits { + sk_agg_commit, + esm_agg_commit, + }, + )) +} diff --git a/crates/zk-prover/src/witness.rs b/crates/zk-prover/src/witness.rs index 60cf85989..ad09658da 100644 --- a/crates/zk-prover/src/witness.rs +++ b/crates/zk-prover/src/witness.rs @@ -134,7 +134,7 @@ where mod tests { use super::*; - const DUMMY_CIRCUIT: &str = r#"{"noir_version":"1.0.0-beta.15+83245db91dcf63420ef4bcbbd85b98f397fee663","hash":"15412581843239610929","abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"},{"name":"_sum","type":{"kind":"field"},"visibility":"public"}],"return_type":null,"error_types":{}},"bytecode":"H4sIAAAAAAAA/5WOMQ5AMBRA/y8HMbIRRxCJSYwWg8RiIGIz9gjiAk4hHKeb0WLX0KHRDu1bXvL/y89H+HCFu7rtCTeCiiPsgRFo06LUhk0+smgN9iLdKC0rPz6z6RjmhN3LxffE/O7byg+hZv7nAb2HRPkUAQAA","debug_symbols":"jZDRCoMwDEX/Jc996MbG1F8ZQ2qNUghtie1giP++KLrpw2BPaXJ7bsgdocUm97XzXRiguo/QsCNyfU3BmuSCl+k4KdjaOjGijGCnCxUNo09Q+Uyk4GkoL5+GaPxSk2FRtQL0rVQx7Bzh/JrUl9a/0Vu5ssXlA1//psvbSp90ccAf0hnr+HAuaKjO0+zGzjSEawRd9naXSHrFTdkyixwstplxtls0WfAG","file_map":{"50":{"source":"pub fn main(\n x: Field,\n y: Field,\n _sum: pub Field\n) {\n let sum = x + y;\n assert(sum == _sum);\n}\n","path":"/Users/ctrlc03/Documents/zk/enclave/circuits/bin/dummy/src/main.nr"}},"expression_width":{"Bounded":{"width":4}}}"#; + const DUMMY_CIRCUIT: &str = include_str!("../tests/fixtures/dummy.json"); #[test] fn test_load_circuit() { diff --git a/crates/zk-prover/tests/fixtures/dummy.json b/crates/zk-prover/tests/fixtures/dummy.json new file mode 100644 index 000000000..051501b7d --- /dev/null +++ b/crates/zk-prover/tests/fixtures/dummy.json @@ -0,0 +1,22 @@ +{ + "noir_version": "1.0.0-beta.15+83245db91dcf63420ef4bcbbd85b98f397fee663", + "hash": "15412581843239610929", + "abi": { + "parameters": [ + { "name": "x", "type": { "kind": "field" }, "visibility": "private" }, + { "name": "y", "type": { "kind": "field" }, "visibility": "private" }, + { "name": "_sum", "type": { "kind": "field" }, "visibility": "public" } + ], + "return_type": null, + "error_types": {} + }, + "bytecode": "H4sIAAAAAAAA/5WOMQ5AMBRA/y8HMbIRRxCJSYwWg8RiIGIz9gjiAk4hHKeb0WLX0KHRDu1bXvL/y89H+HCFu7rtCTeCiiPsgRFo06LUhk0+smgN9iLdKC0rPz6z6RjmhN3LxffE/O7byg+hZv7nAb2HRPkUAQAA", + "debug_symbols": "jZDRCoMwDEX/Jc996MbG1F8ZQ2qNUghtie1giP++KLrpw2BPaXJ7bsgdocUm97XzXRiguo/QsCNyfU3BmuSCl+k4KdjaOjGijGCnCxUNo09Q+Uyk4GkoL5+GaPxSk2FRtQL0rVQx7Bzh/JrUl9a/0Vu5ssXlA1//psvbSp90ccAf0hnr+HAuaKjO0+zGzjSEawRd9naXSHrFTdkyixwstplxtls0WfAG", + "file_map": { + "50": { + "source": "pub fn main(\n x: Field,\n y: Field,\n _sum: pub Field\n) {\n let sum = x + y;\n assert(sum == _sum);\n}\n", + "path": "/Users/ctrlc03/Documents/zk/enclave/circuits/bin/dummy/src/main.nr" + } + }, + "expression_width": { "Bounded": { "width": 4 } } +} diff --git a/crates/zk-prover/tests/slashing_integration_tests.rs b/crates/zk-prover/tests/slashing_integration_tests.rs index 3d84c94d1..9ff14d3c0 100644 --- a/crates/zk-prover/tests/slashing_integration_tests.rs +++ b/crates/zk-prover/tests/slashing_integration_tests.rs @@ -70,7 +70,8 @@ sol! { uint8 failureReason; } - function proposeSlash(uint256 e3Id, address operator, bytes32 reason, bytes calldata proof) external returns (uint256 proposalId); + function proposeSlash(uint256 e3Id, address operator, bytes calldata proof) external returns (uint256 proposalId); + function getSlashPolicy(bytes32 reason) external view returns (SlashPolicy memory); function setSlashPolicy(bytes32 reason, SlashPolicy calldata policy) external; function setBondingRegistry(address newBondingRegistry) external; function setCiphernodeRegistry(address newCiphernodeRegistry) external; @@ -91,6 +92,7 @@ sol! { function setCommitteeNodes(uint256 e3Id, address[] calldata nodes) external; function setThreshold(uint256 e3Id, uint32 m) external; } + } // ── Helpers ── @@ -372,6 +374,22 @@ fn test_digest_matches_solidity_encoding() { const VOTE_TYPEHASH_STR: &str = "AccusationVote(uint256 chainId,uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)"; +/// Lane A policy key: `keccak256(abi.encodePacked(proofType))` (must match `SlashingManager.proposeSlash`). +fn reason_for_proof_type(proof_type: u8) -> FixedBytes<32> { + keccak256(&U256::from(proof_type).abi_encode_packed()).into() +} + +/// Custom error selectors from `SlashingManager` (Anvil returns selector, not name). +fn err_has_selector(err: &str, selector: &str) -> bool { + err.contains(selector) || err.contains(selector.trim_start_matches("0x")) +} + +const SEL_INSUFFICIENT_ATTESTATIONS: &str = "0xe424f994"; +const SEL_DUPLICATE_VOTER: &str = "0xcbceb64b"; +const SEL_VOTER_NOT_IN_COMMITTEE: &str = "0x4ca81c26"; +const SEL_INVALID_VOTE_SIGNATURE: &str = "0x64a283db"; +const SEL_DUPLICATE_EVIDENCE: &str = "0x5be07e5e"; + /// Compute `accusationId = keccak256(abi.encodePacked(chainId, e3Id, operator, proofType))` /// matching `AccusationManager::accusation_id()` and `SlashingManager._verifyAttestationEvidence()`. fn compute_accusation_id( @@ -447,13 +465,33 @@ fn encode_attestation_evidence( let data_hashes: Vec> = votes.iter().map(|(_, _, d, _)| *d).collect(); let sigs: Vec = votes.iter().map(|(_, _, _, s)| s.clone()).collect(); - Bytes::from((U256::from(proof_type), voters, agrees, data_hashes, sigs).abi_encode()) + // `abi_encode_params` matches Solidity `abi.encode(a,b,...)`; `abi_encode` adds an extra + // outer offset word that breaks `abi.decode(proof, (uint256))` in `proposeSlash`. + ( + U256::from(proof_type), + voters, + agrees, + data_hashes.to_vec(), + sigs, + ) + .abi_encode_params() + .into() } // ════════════════════════════════════════════════════════════════════════════ // Pure Rust attestation tests — no Anvil required // ════════════════════════════════════════════════════════════════════════════ +/// Lane A reason key must match Hardhat `REASON_PT_0` / `keccak256(solidityPacked(uint256, 0))`. +#[test] +fn test_reason_for_proof_type_matches_solidity() { + let expected: FixedBytes<32> = + "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563" + .parse() + .unwrap(); + assert_eq!(reason_for_proof_type(0), expected); +} + /// Verifies the VOTE_TYPEHASH constant matches the keccak256 of the vote type string. #[test] fn test_vote_typehash() { @@ -542,6 +580,36 @@ fn test_vote_signing_roundtrip() { ); } +/// First ABI word of attestation evidence must be `proofType` (SlashingManager decodes only that). +#[test] +fn test_evidence_leading_word_is_proof_type() { + let evidence = encode_attestation_evidence( + 0, + vec![ + ( + "0x1111111111111111111111111111111111111111" + .parse() + .unwrap(), + true, + FixedBytes::from([1u8; 32]), + Bytes::from(vec![0u8; 65]), + ), + ( + "0x2222222222222222222222222222222222222222" + .parse() + .unwrap(), + true, + FixedBytes::from([2u8; 32]), + Bytes::from(vec![0u8; 65]), + ), + ], + ); + let leading = U256::from_be_slice(&evidence[..32]); + assert_eq!(leading, U256::ZERO, "leading word must be proofType"); + let derived_reason: FixedBytes<32> = keccak256(&leading.abi_encode_packed()).into(); + assert_eq!(derived_reason, reason_for_proof_type(0)); +} + /// Verifies attestation evidence encoding structure. #[test] fn test_attestation_evidence_encoding() { @@ -688,9 +756,9 @@ async fn test_onchain_valid_attestation_executes_slash() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; // C0PkBfv + let reason = reason_for_proof_type(proof_type); // Set slash policy (attestation-based: requiresProof=true, appealWindow=0) slashing_mgr @@ -715,8 +783,23 @@ async fn test_onchain_valid_attestation_executes_slash() { .await .unwrap(); - // Set committee: 3 voters, threshold M=2 + let stored_policy = slashing_mgr + .getSlashPolicy(reason) + .call() + .await + .expect("getSlashPolicy should succeed"); + assert!( + stored_policy.enabled, + "slash policy must be enabled after setSlashPolicy" + ); + assert!( + stored_policy.requiresProof, + "slash policy must be attestation-based (requiresProof)" + ); + + // Set committee: operator + 3 voters, threshold M=2 (operator must be a member) let committee = vec![ + operator_addr, voter_signer1.address(), voter_signer2.address(), voter_signer3.address(), @@ -790,7 +873,7 @@ async fn test_onchain_valid_attestation_executes_slash() { // Submit slash — should succeed (3 valid votes, threshold M=2) let receipt = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .send() .await .expect("proposeSlash tx should not fail to send") @@ -853,9 +936,9 @@ async fn test_onchain_insufficient_attestations_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -879,11 +962,12 @@ async fn test_onchain_insufficient_attestations_reverts() { .await .unwrap(); - // Committee: 3 voters, threshold M=2 + // Committee: operator + 3 voters, threshold M=2 mock_registry .setCommitteeNodes( U256::from(e3_id), vec![ + operator_addr, voter_signer1.address(), voter_signer2.address(), voter_signer3.address(), @@ -920,7 +1004,7 @@ async fn test_onchain_insufficient_attestations_reverts() { let evidence = encode_attestation_evidence(proof_type, vec![(v1, true, data_hash, s1)]); let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -931,7 +1015,7 @@ async fn test_onchain_insufficient_attestations_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("InsufficientAttestations"), + err_has_selector(&err_string, SEL_INSUFFICIENT_ATTESTATIONS), "expected InsufficientAttestations revert, got: {err_string}" ); @@ -970,9 +1054,9 @@ async fn test_onchain_voter_not_in_committee_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -996,9 +1080,12 @@ async fn test_onchain_voter_not_in_committee_reverts() { .await .unwrap(); - // Committee only contains committee_signer, NOT outsider_signer + // Committee: operator + committee_signer (outsider is NOT a member) mock_registry - .setCommitteeNodes(U256::from(e3_id), vec![committee_signer.address()]) + .setCommitteeNodes( + U256::from(e3_id), + vec![operator_addr, committee_signer.address()], + ) .send() .await .unwrap() @@ -1030,7 +1117,7 @@ async fn test_onchain_voter_not_in_committee_reverts() { let evidence = encode_attestation_evidence(proof_type, vec![(v_out, true, data_hash, s_out)]); let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -1041,7 +1128,7 @@ async fn test_onchain_voter_not_in_committee_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("VoterNotInCommittee"), + err_has_selector(&err_string, SEL_VOTER_NOT_IN_COMMITTEE), "expected VoterNotInCommittee revert, got: {err_string}" ); @@ -1080,9 +1167,9 @@ async fn test_onchain_invalid_vote_signature_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -1106,9 +1193,12 @@ async fn test_onchain_invalid_vote_signature_reverts() { .await .unwrap(); - // victim_signer is in the committee + // operator + victim_signer are committee members mock_registry - .setCommitteeNodes(U256::from(e3_id), vec![victim_signer.address()]) + .setCommitteeNodes( + U256::from(e3_id), + vec![operator_addr, victim_signer.address()], + ) .send() .await .unwrap() @@ -1142,19 +1232,18 @@ async fn test_onchain_invalid_vote_signature_reverts() { .expect("signing should succeed"); // Build evidence claiming the vote is from victim_signer but signed by impersonator - let evidence = Bytes::from( - ( - U256::from(proof_type), - vec![victim_signer.address()], - vec![true], - vec![data_hash], - vec![Bytes::from(bad_sig.as_bytes().to_vec())], - ) - .abi_encode(), - ); + let evidence = ( + U256::from(proof_type), + vec![victim_signer.address()], + vec![true], + vec![data_hash], + vec![Bytes::from(bad_sig.as_bytes().to_vec())], + ) + .abi_encode_params() + .into(); let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -1165,7 +1254,7 @@ async fn test_onchain_invalid_vote_signature_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("InvalidVoteSignature"), + err_has_selector(&err_string, SEL_INVALID_VOTE_SIGNATURE), "expected InvalidVoteSignature revert, got: {err_string}" ); @@ -1206,9 +1295,9 @@ async fn test_onchain_duplicate_voter_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -1233,7 +1322,10 @@ async fn test_onchain_duplicate_voter_reverts() { .unwrap(); mock_registry - .setCommitteeNodes(U256::from(e3_id), vec![voter_signer.address()]) + .setCommitteeNodes( + U256::from(e3_id), + vec![operator_addr, voter_signer.address()], + ) .send() .await .unwrap() @@ -1264,19 +1356,18 @@ async fn test_onchain_duplicate_voter_reverts() { // Submit evidence with duplicate voter entries (bypassing encode_attestation_evidence // which would deduplicate — construct manually to have same address appear twice) - let evidence = Bytes::from( - ( - U256::from(proof_type), - vec![voter, voter], // duplicate! - vec![true, true], - vec![data_hash, data_hash], - vec![sig.clone(), sig], - ) - .abi_encode(), - ); + let evidence = ( + U256::from(proof_type), + vec![voter, voter], + vec![true, true], + vec![data_hash, data_hash], + vec![sig.clone(), sig], + ) + .abi_encode_params() + .into(); let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -1287,7 +1378,7 @@ async fn test_onchain_duplicate_voter_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("DuplicateVoter"), + err_has_selector(&err_string, SEL_DUPLICATE_VOTER), "expected DuplicateVoter revert, got: {err_string}" ); @@ -1326,9 +1417,9 @@ async fn test_onchain_duplicate_evidence_reverts() { let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; let slashing_mgr = SlashingManager::new(sm_addr, &provider); - let reason: FixedBytes<32> = keccak256("E3_BAD_DKG_PROOF"); let e3_id: u64 = 42; let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); slashing_mgr .setSlashPolicy( @@ -1355,7 +1446,11 @@ async fn test_onchain_duplicate_evidence_reverts() { mock_registry .setCommitteeNodes( U256::from(e3_id), - vec![voter_signer1.address(), voter_signer2.address()], + vec![ + operator_addr, + voter_signer1.address(), + voter_signer2.address(), + ], ) .send() .await @@ -1399,7 +1494,7 @@ async fn test_onchain_duplicate_evidence_reverts() { // First submission should succeed slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence.clone()) + .proposeSlash(U256::from(e3_id), operator_addr, evidence.clone()) .send() .await .expect("first proposeSlash should succeed") @@ -1409,7 +1504,7 @@ async fn test_onchain_duplicate_evidence_reverts() { // Second submission with same evidence should revert let result = slashing_mgr - .proposeSlash(U256::from(e3_id), operator_addr, reason, evidence) + .proposeSlash(U256::from(e3_id), operator_addr, evidence) .call() .await; @@ -1420,7 +1515,7 @@ async fn test_onchain_duplicate_evidence_reverts() { let err_string = format!("{:?}", result.unwrap_err()); assert!( - err_string.contains("DuplicateEvidence"), + err_has_selector(&err_string, SEL_DUPLICATE_EVIDENCE), "expected DuplicateEvidence revert, got: {err_string}" ); diff --git a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json index fe5f7e9a1..b077dcf15 100644 --- a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json @@ -1,10 +1,10 @@ { "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x000000000000000000000000000000000000000000000003f7062d91e599a1d9000000000000000000000000000000000000000000000008cc7102c928c65c450000000000000000000000000000000000000000000000075ba3dc02d69ce9910000000000000000000000000000000000000000000000000000ed71a67536b000000000000000000000000000000000000000000000000e44e8edaa774d8d700000000000000000000000000000000000000000000000069df7c477ac356a5a00000000000000000000000000000000000000000000000546c35bc9a1e69c5500000000000000000000000000000000000000000000000000021ee487cecdf100000000000000000000000000000000000000000000000ab881c361ac7b250800000000000000000000000000000000000000000000000615484820700c856a00000000000000000000000000000000000000000000000bb5ddeeb0299d72f60000000000000000000000000000000000000000000000000000614a8dc34464000000000000000000000000000000000000000000000008f86b47c227146d86000000000000000000000000000000000000000000000005b0124c6d784e70a800000000000000000000000000000000000000000000000c7ce22f6009a0150300000000000000000000000000000000000000000000000000028c352ad51c1f0a1e934d28e26ddbc2239f70f1f2a218f011eacde911ac55ca4f56e4c92826ba0151ec8baa1515407b51df92e0368977054f4ff1d48781d8c1bc7962286ed7d20c520eaffc4f8e6351751bd8fe1e162471d0fe3163d177d0ead3ac22d2519a17089d86113a4a8393c93b80f3bb7c9dd5bab24c560c9f0d8906322ab4d14bdf2527575e8ae48fb40af0adc9c7df54fcb65138a862afbaf04d592215d462bcd29107c4ee919449aaed15296c3db7bc5d246d1753696a3fb0d71ae9e02085318c402afd41a4633e857acd0967c13bf26c50c6457ab5dd898ac1864799b9730689d52ad93c530511536a3714b5748aa649245ab18e286181818ec79f628930d4b39d1af275a9bbc79f11513fc819f8a77429a99f5c19dbc3aaa699028fd84578ae771d2cb42f9080e97577d5433096dc7b5872c8a57058cf2b02f8c0e531169ac86d010bdbafbdca84bfb0e04bad65fe3350d55fbfd17c3806f415f2fef12c85a5cb05693555777afb1ed8c9e98ab2221191471b57f70880883d7f77cf7774d4fa8726d1e7542a8d79306c76a221eaf68d96dcc25f7bfa67547c7e7e6823a8f5036a0520516bf26d60b94a8dc1de281b183f97c760db8ac7dd87c986a7ab87c57a3521148a502af09952b50e41ce4798bccddf61aefc5c08cbe35204f43d0c36954a0e89229a121eb3d18ac0c9561aed66af56e683b1ba590eaa4edc1d6476c2762208096dced400b6f35fcbf000acf60ad54d9076eec61d80121dfff0afe26e749c2c7dc41f5ec8f3774ba1084aa7c4d494798c31c4b33f0371083a13e67aaaa9fb297e0ddb44886687d5d35b7485cb47105303ab9d1a237f8dd9dcb93298077c2c1bf65619bd87d561ea96e41c5106896c2a1307eae3afc8ce2c501e6ae5183d9b302f94e692484cba7c25419a42dac6badcb139955e149eccdee3c1b2bbb39bd92006d24ae9e0b0607f5c1994f5c0eec60fdd95dcd71c256fde7e12934ae95ff62c1c3afe38e4250234c03b3fbe574ebdc4b6acce335cffc79409260039c2c5890f33e7fc5c1904725888d81fbb00dfe5829b22b86c60cc60282639614406840619a2a11d45659615c74b56c03d94902360a16b8813b318f90cfdf6d941bd27c11c2c40020df0c8df1b5f56cf8faf6e3f78bdd34f134ab0857d93cc7fc0d8d26a084aeb7c91a1d79ae529ded891a7a2a94b774927a6747644bc87afa45c115cef199c133466b246d8a8f6a87f50454203689ffe4a63c722e36cd714d94d5d8ec715203e52c6915f66cbd5ef50437c703b5c3393cc645517d6c2a210f9ac23d88a2d480d46aa3febce868435cb4052c6af52d836aa60a7141cd972100fdea65c6623f51577a4fdabe52375af64863d09b294de12c786066a4510cf18737b5cb2521bbd58b29c859db5be07fa8057c61e27972541464105d7346270eb1eaf8183f00b03e6634f391192f98965c1fe150acaf8decb6acd8ae9578aba334c8ceb816f220f0a141a0b9c666e712d8917ceae41edfa816a4a3cc319116df04818fed2c5199163f3eb27e7de5e4d47b7a22a1e7e7186dc3a4ec5744e6b26dc0ac4fe236317e300bfa4ac8d1c52a22a867753dbec2a0ef3ea56b28a978dc21c6d56e0a9822cab56055b0a192a4f2a1fa9a1e2ad0d98fb85051bad9fcefaff281aeb31781b070f5b929a644a885cc948d2b8e9b7948b588a829b836bd61f9087f753533e5c0e79b5fc84e0de7671e00294fc3a78e2ad1e7d469a0e2cbc379f6494954a946d23e35a296d5c94f4cf03f663f7328be01a3aa375f6adfc6e3e10f89456df78a3240903d00116a9b3572d29437bfede50d55590b4df762150fbfdd6fee2c519302c37cdbd0ca0131da0faef83a2b57f89150a76f67a22fb2825ae844a2c0e55a62c00585cf13f7c290242ac5610faa0fee3018541c5733b68415f6f999951b90921e98735f1adb294593b17e6678722213e8c446ad3a7ac022936b20a6d77b0f6176f505aabdbc699bbc6e19d082fb8318459555069abbba3d01c1b9b552da6c8174ffa28093ab52aa34c5a04e72119ca3dbeb427c31876f8a012fa6be8dbd2a81076baa410047ec186a7ca713a00714cf370d06de1847c65f81e0023042b1f491eeeff34d3b6e787870dbef2665bc02dca4ec8c0fddf496de2e31c7450d626fd10ea411776b22e3ceb7f3891f81080eabfbe32548bb88663fa270ebba72fc8c5243643188bc9ee4d07de409f501cbfa49afec7c87d1f2789d2f932f298ccc33c0fb18246887d583e8a5966746fb3bae0cb7f3053e7d807c47ee679166cfac9f11031de5328aa1e8cbbfe748655b5ac3f7b303a92328c64d738e337e2c88e4b1815aa4e0f6573d470282ce50581e75a13f328a3a0b9a1c263f5180223110576da26d8a27a0e4e674729f076e96b629ad9b2501d8f8d437d8ed00e908692403aa12b370df72df67acd5d66fbd6d6e9f3341101c4fe455c6429cbdc3d843b52ae36215a43ac8698db6dcb1ce12497b9f51d8cd0c4b78b96bd81f24d574177a98f2c19090146ab59f7d5f461a0c6bdad1e75b6da38e9147b88a655ca582581ad2b64274c7a63e1cfd278616f7d185902cc57cd18f89bbb4ce2dd17cecebad3c3ae812450131532f01a9c5c62f6bd9dbd817f5b7a07ed301ed9d92083d0724cf8a2221e263a087933e462f1975e9d2aef17772cb92841fe96566553bf90ad051da81e0bb61346c75d7de2a99a99001e56bd9dbb08dab169ec147bd7da07e01518d6951476e1758afce02df80c3fbdc515f04cb5bac48c201aa8b1fe54a520908d01d71def68a4993955aee64e9ca2d4b85bff5404f51696a7f26ca335b112900df31029b365ff086b60ab3d13c2c44465a1656c82a1472c6471bd0a018542139b4bee2741b75a8797dca4cdea4df105561471e1925e46a05a495ccb95a62bda8f2fa905bf990e5f7a30b9ec101e503b1c44ce94d6d751db5d4d01adb185e7e3de9f27268e33e71fc0136c349931194a638570f3771419d2b2dcddd74580e61cd0e1d82651a4f74f8bcb480b02daa45a841fc3f76890eff255b85a88bc204442cc35b501e65b950d5618397f528e8835d8167c888594c0b3d3d56335e4968440b7579c18a71da291ff2dd20bff984b0809d0a72ffa9aa15e477ab40e998e1845754979039b052e98b768bd8ec0df44278c207269026354eefbf6d32494347009c580731b668298f1615b5fd77cf4d43bf2f6319db631859267780752e728fb52d39eaa1f8794c0aabcd93d68844a49b146200a8dae0284da464d105fb70dff102798180231d29917b244496fab251ab80197d5b680b97fc6560a74d4627bc8702da1430dd7c2e80e72a633dc52613da653e24c43173f0374fb9ee7c31d45109a2f2dda1a6ed6e02c387274f3dd41d3d30ebbe2d0e5be93a288321e54eaafee3ae3408d2a209a285a1286870d7081d8d7987d96c0d96ca7d38d53de6db74497ebe57c97239e24d134e668da1e614fc31b92e571228eeb9eeee087b5e87183899701652d1223a0c17880be1879a41e3e4444a426ec0e9fa4af9628907a42e4a40d9aeb57244d40642f454a684f07fb45974600f06e72ba7ae4dc8ebe05a4346ff12279811e1e8ac8dabd36dc3b58fe17eb6bda441f9d75fae5a431f131ef567c49334a2026b1b06d0d09e4dc77fd44f9bba7003e5f6b6b6a78d19cd2ddea68492d8d5cf918fd982caf0cf885d3a0819ffb8da3d24352207baa1802784bde523e1d4071772552c303070de53ae5a62aae59b858aed5260f66cf269d2fbe57df7c3960c84c00ce310e336aadf9d8ac732b8504f61775296afd57ac458c4b3f587e9095473628876de23bdcea9df2fefd44148b794984b966027bb3835f4deb5d0323e3d8512839d9d7e91bbd6d0b579a8269256fadf864f388ffca58afddd505f1009a887403f654353a693b5449c09424816a4f223999c0261b85fc9e27f8044a685fd97e21c348d3131cd0ad8ab11361ad8bd542e13b95cc94bfc6f4e02864e77bc3a5ed27eadddc4e835359c11b295e862d4647e0be8151f1472a2bad617d662049c258270ba05cc3c8fee60b79dbe3cdd5892d467eb1eb919b10908c1e6bce7cd6256f08b90d498edff9b990d45e8517e68b155bde445476e86361a62bfca66c782d9e1d5020f5aac68add0d8d5e11313bd34806eb1d07998bac8aac78045bf62ef35d1e6c68699ae309a357eb91767cc97f41afc839fc0e2acf149f03f5e4768ffe12239d5ec835b63dedd8ef257ae0ecf932ea82089a9b334f50443d89da8b9d940d17cf45a436d206057e1baac60dd3d4fd5053184b5fb725563e2f5dac366e47a41671e27bfa1af5d7604965af6f65f9cbc0e187826e3034396ad39269a7828deb009d86c1876e90e2bdaf2310456e63048c0ad17f98d7e3405a57c62f0891862c22868eac6f10b58ad7476cedf652fcb48c170c458ea95e0dab1300d1477d371a0a83855b2b4be0f913d533698dc00b61e6e0d98172e11cfecd9f2ec88fe7e1282a8e3523974469c524c395b19cd1b2ae2e6a0f08b8b7e9925dd16241984f02fd028654ca0f201085d96833d6c571a08c2b680b77e6374767fd732a315e12b8c0150e7ce2da2707c402b007853edf703e5a06659abccc5c7d539a6303c10a747a1e1e6c660a3493a44ad142c672094981fe9b0699eb6202ee481875332cde412924e4d4f54c75ba290478813ee7b7f8d2cb73c42bdc9b4db044a2c033356a397d2c4c27dacccc0604af0ab1b240b25d79bd8efba3cc58a7d4e98a56ef93e694d61dd9e5f7b64e66744fba85443990e2ed06eec8d4edba99ed139bc3fc816ff1e925b004f64a5e0fad8b437791220910fb7619b648984bb76620b9995c1e0a69a700c51f7b25bbc228f229520d7655200d19e0e155637260069e8ecaadaf56dfda0b606943398c2acd8244ef5e50f797377dfc6fa9ee06f4275f03912c8b039a4e10545dafb7eb5d56f282fddbe00bfa22c42731fd13dcd08237f88c870a6b02d821c0cc4c3cbaee07da622638faeca1c035134da588eb3a47298ff1f76c5bcb1b07713872aefcf57c09befd5274046db1e94bbf346a9618cec87c5d833b3cb6bd1fb1d5fe9d37ad9b8f7d69d27e74ee6d11442b4072971d6e308c90d2f28f692c154ec89bf6cbdc150ce4340973cbcaf071d3a6b86f43c96c95bb68fe435b719e204f06a549b9859201bfb3c8f674334d15b92441bf420dad6b8ba7ed93a4b10d2446d9f477b8f4ec9d7d61dd89ffaf24851b9e1af79ef09d04fe4c7d3b8aa5fc0da2c0013fa968fc23f85734436cd57b203429df6c86e228b25d897c432a70cb14e82c91259ac6b99a472f04cb05da560a649d84b1d96e532adbd39ce1d118e024519d5d192db13745d58bd16158c16f79f890268fe33acf3640cd7fbd97228a0ad8f596ad7c30f5fd9a30c3a58d741a2d1248c01cf56fb12d646351b1d7bdec09b42a222f01fd7c10403cefcf03c0fea4708557da361d24f964088d362babea2032fff91add268b7e2260474cc0c1aae1ec3526be33d9724af9298ae7e94117125ed9034842758be7da9d9f46146f157bcdf61f8f0ce96b32970cf633a17db02a03183311f34d9192fb47068e905383e1fb9a893dbe99e7852c091eb056d2df2a36fcf2ea043a1f9f80df7d2e61ed2d3f3294a2bd6ce92f31dc6d8bbdab4f6d1c9e678355d1fcf76296effeb331d4e2fe67a71da03da01f1a18cf069553eaaa13980e46467f02c69bea54c3720548b3123a35dc4d419d66c373b9089ce74d2f1c887e07f9068b2b4151380e03ab3782c4d3477698d60306b49d96880133708412e7aa8c948838ad2e7c574a87420d2eaf8a25535161adfaf0c5dc8f246e6f831f0b0d07d5c1786ecabd3ba2c3c693d53d95d3c61376e795acc867eeedc7c22e069b5d13c0155fb52b2f949bacdfedadeebf5db8d0bf6c78a11124a650873d00014105f5c4d5e9a76723d3c67939a223b0e21dd47410856f6109731f3fd4573914528d4d0451b6e9852c06874f6a8f499644f82ff8016134533e19e7543937fa1b79e556eb2a677f43c40ec09e8c2385b5e5dda6215ab070253c5d3e53b2ff7a00a2bc71a7012e60f4ac98fe1840e7be8c09aa648b3ab0f0c27085569d6460b8028f028a0fe35cbc839b5c9e28401bda72c3972c2888d203f378d76751e957740c1177fdc8d98ddc08e28b05c069efa51c8252cbe0cf818abfcd395484981c0e2b80cc558a8a10aab8b962daf92a786433511af9f8527aa2a49c2b8bd53101d105fc145055d9ba564709104c5549bb24a3dd91a505e47959f719258a1cc6dccd06c3b905097f428b5cff87707bd0e1ce71ece41f31ad817ff1f3d04630274e7707bac1c17fe327bb81353d07980eb53d4602c580dda7f7f15e59e57cd7dbd3d90613f1fd884030e3bbfcb0d7efdc94158660e2fa5c7d49d11e08a9b723087892209124075b7d82394c4ac6a6a3e6744b706cf7f538fbe24358c280b1c6d241ec04a10e305eb956910f0d968d0f83280049572e3e7206b91ca83f558ff6a01694227174a5061617717a39664d6b54a851e1ab5c9041c80952a220f62d183fd4ff02f9d499f6808334fb92a96a4a1332be8b96b264d7ace5e3dde89aa066da81471f8dc4f4c40b4ff98e4b85557de6e994baccc86846ab05cd2e6ab2f7affcf99c008ccbcbedd42d97aa344146ad1359db6ef1c3c8e7ee17fbbadcf7d53c7b749b00d57fead071621cb4046c8d9c40b138b1e897b30d7872b654bf975575b18b540e2b702474b75054fd96c52543d85e68751471a807905b532f7b72afa62bc86c08d20514704803dc419c284bc0b953f95b2c5c957979546e79b46c2596fcd7bb07804adccc6532e423df312c056d852c6730935f95c1ff85556161503263da9e10b45be2e0cfbcc72420fec0109fef3fc1d69a1610ae256cda6fa0b1edb22e6325599a344ed9500bb3b6db80b3ed4b856d798aeb51be3bacd83e3ece79a68d750423ee12cd58ed06961b157814caebef95e7bb0384213e8f34c3f364f7e0479b248d3ce8d2005755b5832407fc03cfdfd570903d70ab82f55e01499f0e533dff1d1ce6f0c124b0fa6dd880e2c715a08bd9834659fb5dad9de7e79fed5471144c0caea76eb72fd11995bbcd26b93c3eeef7bf4613270088c9d292c5b16a2b9c5f064ed3eb9f013c40d3b53e08486724ba493c06a4d9ec7d768aacb0ae7e9168c0028372c10a7600ef9e464c2617a6185c7f9f1517cf7925c3516a0bf6a8f6e7e229a8fa104e164b8cb328d16eb11f3ea40f968302cd6aeff674f113a0d5f4eec61d57142b4efbbaf733db8415459c129ac5721a1b1bea5a025da135349fef2e86031668421ede6358b63d5078c2bedf744d0b9304603ffa2726d02e8c9bedcddb10e57654bddbf2ff23134fd892ddaff5e13c7571c673578d336aa4cd6f7ca9f30fa7ff2cf4ea874d55be1e755b749a2246ca93b7bd613e24c52ecd0e0190563713f2df9406088e6b2cd92d9b03b4e3affe99b7624321bd6b462fbdf3235bcfe52871605f0cada9534cc6bb07b31b133dea6b0f533aeb70ec48236de198c427080056e3d16a420157d335b08369f69b8897645453e8087753b82fcf8522406be11fbef23cf74e343dd70144cfa5ee7e67ab40a2dc21902315b46772eb4275bb841254da0b779691c5f56122b367f86dff07a34bdbc053a9e56dfc4afd69fa3609198d4cc852a9b2707922e89d1b91ebb02dd602dbe9cdcfc74022e67aa0eac4fd1261d7f338e3f31aa22e69b5d4217900caf7606bac392cb7b46b96b21305ed0b0781389ac7d3e23b955e9358d57eb4735c8ebaad59d1f2349dcc16bdaae8c0081d29f21df52ff42d437137ea915aa96d1e286e1a88de177775f59064fb4a84fe06aa67f3a50812921752d0fc249387a70a15aaf42811dcbbcde646c49f404e8f09121fd977cc344c9126d5a8139cc5afa4c15e555805e2edb6fe4a26460990462fabe10ea395785faa72127618e810bdabf626a3e54f78cda87a4d3c068c09e10663bc532763b0861d40cbe1eb9e8733ffc701df12c7c72a824b1c581aec615a1041c48075e3a7ea820617fc48ae9a815b01b78ca7dacab62c9f4b32028d8e0e0d15d296790ffe2507bfab1653f0e13bc3e70337762110b30e0f2e2c7a86d2ac0bdeb5e0f0ec18899aac0ae393b62e1727b189591efbaaa3853735ea9073d90115f3212a4b7d53472fccd625def7ea36ec894cb964a6dfc9a7ccb369d549c0a125b1f2f6f5fd4fa21b90dbe622eec58efbb1705e26cc444bc5aa0671d1b68d961bb5706653c96d8875eed434702544d6381308c43e8fddb0a5640340a64a287b20ee4d26b56ebf145ea85a4315980f5c1f4e64f97d3ee7269de80f1d5947c1a61fe375768d34e3151255e55d0f6857e895d118541da36e13ebdf6eee9e981dcd0d9b0fb6f9fab694ba9973b04780ae2729596e3c425dc871a404a2bc36bf5a7300e0cc098986f2091345a77f699bf2eb03d648c8f8886e8e103faef891f1f3430d57c6d441288ca100d3cb75b8e3197a571fef30525f4b542350d71170de7b63197434f4adf3c4383e89d67c298c03447e65f8b572898d86bcc8df7d5df4110815a16f3839c72f3ac89b895876947140d4aee3cf1d7ef24a44d41703097a6c912c783b7b2a2d525b86be5b993cee26e637b892bd6233a46d0412808ed53309a126051ffdd0d5e0ba154d1f3580a24c5896d172b523c26bca9f2cb842195ff17803998f55cb444e663539aebb346cb75f5dd2f096ed1989947829c17059d40d3121f726cfef977699f9f5f4c66b9073262f539766dc48a053936113d6cb284ef3182d0a64eadcc863d7267978ca1d1afd73412e3fddd3be739603e57cb3ab538b2daf94efc9e5d3473bee3b714734a85db1367c08b05bad992d964802caf16e360e0e85d8f672e27ade6dd0afcfa6eb56f6181729cbd29af3e38b3b4ecc8a4ed724b723a3ad15890a0520a9cc26af542c1092d15000e9f050f8ace54f722fca9b0fae98c4af61f623dfa3b6547dd7c7f269e4106d91c1fb7362b98c1d36f73195116ba3d41cc2bb48e150c70aeb89257d4830e1c270cb936c127a23dc0c6fc7f62f8d6e4417046d79ade659637b6ffeba9b4cff1906a2088e5f6a9609aa704f9a1165b54e18ce8e325c8671f0a81f9263ecdc7efebbf9b86dd008d5f5f7cee56e1c021e2fe60ad190defec566a5d0bab3da01faadd8362daf771f33955181342d11e1a24c26b5d76a39cc1cf7536241ecb9ac2b798ded0d845d1a27c4630316c321b3545192fe684d5ac406677e9bcb389409a0bb05d0cada8421c5344a8cdb750575e263afadfccb386f60fd58489d0bb4370f58f4dc12936a0c1f7d94e2faa22b29adb0e175cc473c2b2b52586dcd28eeb8a244cc8cb76d5f6fa9ce3842a7f61c3e506b19db42f1e4cd8a4b05faf174375a49a3345d08b0c6544bc814d411b21b8e0930d8cbcd0a0b12d146fc1706a84570251f07d63229e6b54cedfe841b371927bb245fea1098b2d4893abe28bf9cf892f53e29787044c915920868935d1005f86006eccbb4c753a380a3f46ac1467db33834c5acdff2c04978e0457583532f0610d3970d4a9b5b3838447839d1fff0506f28421b675effc4974c3bb02bd22bcd664be35d9e5cdc1ec748617d7ffce93e68f835aaec582277ee1beabf7abe2c63fc73c84ea149425c4ae255b6ef597aa1daeb19f6d5a9aec78ca0264ed51417192e91d71dd8c4b1687cff223ea19889910882de0c4f273b29045dade333512c37500afac03dadf9007c95e3ebe1eb027df638d698a535a9ad67172499858e2032af8d2c6452d9b4a0f925e285a6d03775ad1ef89ec446adfb664f078fac8222af34ebfef8c8e5c5d07332510d9be767a2a8fd3f63fc719180097b492a444a1a6f28902cbbdd05dbf2624db029395faade8c1648f360c5bcf6f00969509f4e2fd5532d79e87172cce7840ae5b302068c4e799bff095a94ee7ae5461e52bcd22078a08923c52e18c127deb635785ef81c67e7244e64c1c377396736dffed3ec1692cf5b8c4f2ea3fbe22fe38682a641486f2c458d951ea9d5fc55680cedd00803fadd8ff3aa101e11c264bdfa027ca68801f1f4c873b99f1f3b4a40abc376a800d97f88e29c845d8d9046509c88f955f9ca06da4815e00533890681dc0fe6733020b784d02b21d43155fdd5073bbcaf3e1566bcc390a9ba9b063062a48a9f3f2a5d3bf4bf23bee163709fde91c96de0c5a68fe89f2efdc3ac38fcb7c1db40c61a51a42e9804b0b1dd73cc84648a2281fb4fef0cac1feb14770e9b801ee189241e0fad6797d640cd9cf2aa1f55f4907794b6a3e4090a0074f1649e835d0f72352f14bb17efa94ebba89f2cc195d19c0a4482ec507bdd852b55c79e9d8c59b6bf0ada7730dae0434db913ece0a06828895b07f39a3e6ede7e9b2c0e1466188e1313c67ac6c4f1f57e5f5557f09fc8aa8d80995ef0505ab76c174d775f52d340d313f9b536af11d44cf8cab2f740329ac458c884671dcaadc9f91629eb57fa5e220f89ead7fa2e99f3aec56bd785433322b26e444bbf3b9ac16d3fbdbeb5caf9881cfe4342d8f74ddfc2b008e5e7f36a2441b8f69eee1c4b9b5920e6cbc1adf92f2d06bee425899d3e1103d87dcb7d890b87cab8fd75dba3d58da32f6b43f6b72a1c68b90a5619cc74439f23f6bfeda6e7a8bdcea0174649192ac8a3077f36e7212f0ef21644f05fbb965313c6b59f1a3f0e5c1eb66a2e4d73fe198551733e78562bb24ecff59b56bdfd4c04ca5ffb49bcf1d8eb8f47561c76454dd44b47618b57011a491c2c7b0c8f582b70a6dbd8e973244bfb585aeef41986969bc766b04dad2b9029f14ccddffdfc94c9c51aec7d3098e666ae5a374071a1d0ebd51e1a2b0a105b7ab3c6ad17aea375f660802b5fa962f9d7c641ade8f21d8419cfe672ca5e2881b2d7d1561136e96ca186e48709f2a388aacabe3793580b722c8d112a0bfd1d0c0af75def97d5d4c73a5cc62be153242459dfb1acf02bdebf66775fb3c52326917db73597a5134cfe5c0aff32db8eb8e8800ff2a98b25d2693445b93b94201097455751c08f78b7f961ab6bb7b72286b293c77d219585c66f12c49921c0e90e4878e1ff88252d4728c52613de28c758fa1fbd8fe1c563055120ac8565f85f0312a3c57ce1e84512a7e6f01d38bb3649eea2b549a89f4c7c9e8d048c6cc5d62fc8399a0a6f85477d1a11355dfac6630d01abdf9ae7612169430e0fa9d77c2118752bb4abc4768d0e78c8c1b258206011ff5114d300e5f9cfad20895f4ecc9a0625adf0a0ed53181adee44f47cdd416e49d035feff88a30d11e73e404098a0b2825d765e0c53f5ed8b6b0050ab489e5524616bd7067728ea8ef25bf42ac3f020e4f9b1e6deaf12a3336ece22c90458e376cc588ec99ad2985536bd510ca9f600f856a8db2170bf53339701ca57eeb9ba5bbbfcb45d3345eac67b15a9a7d845716df6841e09c71346046a67fcb5b51be9129c2d0faea4eea5f7af7fcacdddc530aef1e9d1981bd9a78af2a0d502bddd3748095d619210cf3aa8705c8492e156311474629244272eba57adaa7e846c3fd8fbbaac71b25bc3feccfa94e205e8fdb02e93809f7de44c8910070ecb3ab1dc6e088dfd54655f84063e4ad7caa07870e257f213cb9ba4e34e721d51d8bbec887896bb40502d0f4646bda6254059093df14e661cdf5d9fa1a86e2f31eeaedae100990227762b9f2d8ec60afecd697a2d02268e342042b0d69d47305455a40599e01a483736fc1d9becf554c695b07a27a27bf096c3282aad2d900c6b7a8fad55643307493f8f5bdad4c3f8d723a97c8a701fb4e51bc063eb1d8bc47ce98ad16ce5b5ffd15b5f7074ed14e9861552e994912401846c12d1ff7905485680f6a0a49cbf4e970e5e141863a92f64c2bfbfd1a1174f603d2a3e484cffbec5010c3525dc2c6c8defe0c84bf8fe3cfeaf297ea30291d98fa5c334b60f5b157e576a25f1bc5ffadd50da6da10933815c8c19605ed2cb08b7a1e4fa23741de312de8b96e8dc72c45770535a0225522978f46cf1ed21ab5f36e2809661362bd7d96bf9a687bc852ee38c59529e06742092694ee965306f7f63faed26003a1b3a0722a267a68dd9d8336bbb86153a483b6be5e6d09c82d52f1ca592a4fcfc91aa516c7b437d24fc0cc47ddd3c783900840ffb419c37d28cfc87e9bf208439bcba9dc1b3d8f3a8742bd2d40dfff4c47a402f93cb70ee62ac421452d7f50b41ecedadf39bfb01afac4290ccdb2c506570a3bb837c133ee2b7bd5cdd4f38fa55aa486fffa202bcf0bfa43707f66d8d6dce1da6e9f42f0440e81894e21855aef5b8a1099064194de5d0497407309360e77ab62298cc6a7a7171617950855fa6e0cc6d3b58d131d266a956d1487a5e73347d133f3edcd09f0298a0c490d03d5fdf00ba83fe139d3b3c0eeefa89e3be6978224b59a8a76751d077d4aadc1d2c3b7b8b03ba7c0d41e798665b99587a1349585699f793e882c17151d330f5a5bff59500819e8e6940a17986979f5806dd2f482ab65d5535c42510937a1e876afbf69f5a829709008a489b9424cc64a5056a6aa6940a88b8bfb1b0022da31d46a05fa6e2fe2f0ca37b71ef3294c374da7f869f5c5195a2047f2f52c1606a44856951fc2029a1c1144932a1a8347c3178f7353972d05d6dc04937816a787ee7a67834a972e8c73651955d9c2820d0941b2b9703b7517ab89cddbca15c281d195c64d5968e0b04f50d10f46714a669d7b5b44a498bd3444c78678d219ceda4cea398aa096457e1a8c148cf86c655ca7e1701d895f3a6c35b9e43c95305b3ef3d50f8b2ccb50a77f64ccad9105908ebb80a1b8a2f6a4cd61fe25c453133f46da139b495892c89059549bfbf6ebf55afb5a56936f9e0c4deebeabe56f1cef15bd45d2c8c4abde84c0c5335bf56f1f298106b3b43a002acf52f1bd330825014e510e11718c2925f353bcd7f7e8b2b09b57e4e14ff26b88b6fd0a94dbf308f688b7fe963cbb6669ce06450fba3bcacfe8922a30dfb5c4c43ed85062969f155d0fcf9eb57301a20fd246a091ddd91e58f93c527184325ebf94c262fa2110053485c49ae2de63239543f5660d218f531efaeb5619bacaf6ec1a531a6f3e4d2956f3fba0a2564c97acac533b19e50c7f1ee04e0c99171bb2dd2e62c3ff384f10b21e392f556dec389c7004e57f81976a8638172ccef2a2579de9a2dfefcb670f06a188216fd3507683b4225cabb350fb4debdd0955fddf0af3edb592f89d5321f89e21848a664a152a2f10cd30e5c3062a73f01fac6d93b18df54a98507d7b24e1a17e79d5cd7d7a04b6cf687f64a54f769ee6c3e63ea939943ba3ad71ba7204d2d142645247e71ba82abd6d8417e3e2af10c1744f09afbdb83348b8df388005fa197a28bfb1f8888fa91ac2f37e7c50740639428decaffd29f3d04c45736f22ba59f6cc8c9b9312bdaaf2ad0eca04c6a8ba9e666243cefb2349cc162922e90e82555416ed28ccdf080cd47acdab87e3d04fe234f5b61e56a14cfea2ac4d82142458d38c7226bb448488b603508251114422bdf608ade285070d3fb8fa19be1efadb5e98dba8fe4a0c9bdec550f90ab2bb34ed5fe5ad599d3edf27d5a35dae2aebfb3d296b07cb8f99c4a1c206b0b6b59271893eb9683fe948e6d393664831038ade85996477320edd744404e4bb9f874a5c13b49d2f690a5decca2551ace314736f8cc59be9a8275245c14730b58bfb696f7b903f798b87a17aadff25ca8217d71f6dc06f7292dc614651a3a12e26d678f12bb20f594e36d8bfd1eb61e6562221acc3c70b28ef652043eaeac624d8d1680fba70ef5f8d6e90125087ac8520075b5dec10f13ecbe75a01ced0777297615fba720f5bdf4acf77fbb61fdf6f85281b7cb7e98388ffccb8455c128f6c741d1371e4eb77ff31254000c5c7b986ec23cfda8e2c9d14b82ce9c8786199b8fd5803c96bca8cda0c50d51a31f971ad900d6fc92e57b5c54cb1628a4ad1caa3a5d00dbe25028a0dbd729902088213881229d5a5053a0191ca849866e654f9bd5ba96aca03f1f4124ce3d62b30f4e7998627f297d970ac0b1b53d1d6f6cb845e9c5ad21900150d743eeda160d5ffaed2ff2d85c43efe8d4a31c58ab21732c303232c3878801a379941be034b86223be51713a14bc8f59b8f103095c109e1ffd7adfc4e6dd8f8b6a6aaef1533c482e984b3267ab716b1185ff128ceb78a0d6c98cab05593591c276d0a646c2e4e0d1df6232b8e4a6ac844c7e6eedd02cf16525b9cf067ab4297d849911d9fb588bda630212f042499036156d5eca74d67ff132476867da3277ae180ae38530a23e2ec291a03046c5c50343b4168477e8b03995fd4b52921f85905314d69292b0dd5bacf3f1dd8a3d3211d6021a975cbe496fbbb92db2d214ccd233f12af8bfaf2593c5475177e16836f7ef420d8dde0217b20aaa70b79fa8a173f7ec8cffc3f3d741c6ec11a097061ca3fb6798ec89e5465d56f9f6c56a0f34884f5fdbf17bb467fc428482c033080d26b788806d4be674826f6cb45272eecd195719cbfd29456602e931404d8e783245ccaf9ffbc85e89bcb0a960ef799adfd5306f5a11ff98d386c580b2104a6cb3d665f49103cd770f2953ba77ab813c925e1d3d1befa01320f989200", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x0000000000000000000000000000000000000000000000088faa138749b3265e00000000000000000000000000000000000000000000000ab39d7744ee99e93800000000000000000000000000000000000000000000000b58e3fd596adfb3f900000000000000000000000000000000000000000000000000021543697d80ac00000000000000000000000000000000000000000000000303b482f3002f24d800000000000000000000000000000000000000000000000b85b3e53031c19580000000000000000000000000000000000000000000000003e4b73366064f452100000000000000000000000000000000000000000000000000005aceab8040e500000000000000000000000000000000000000000000000f5dc05a7bfc973e8e00000000000000000000000000000000000000000000000a5d05f4eb90bb6a3a0000000000000000000000000000000000000000000000060872b59764066d290000000000000000000000000000000000000000000000000001421a9133d75e00000000000000000000000000000000000000000000000f4780863c86fff52f0000000000000000000000000000000000000000000000023927881880d93150000000000000000000000000000000000000000000000000dbbcf61dcefb015b0000000000000000000000000000000000000000000000000000d0ae97d8719c1b55ed60d28990fd534c598c83f2d728a5ecfb56bd465a081d6618cec0292cbd237512e8cbd2be838836717f3a7d3b973f5b990c5a7f1ec421ace3f47a651c5d00fb5ff8417a44f464db9e3db570a7873bd28a5dc9a6bd7a85067e4836b68ce825c611f8f6ba443658991339a5094e904964bb9860defcc05dc2482f953fb6061c687a6373bdb5019e78346a3d8b4aa6358b4b56c32b61afb53bafbf7c51ff7415b3a062e79c6977d6d8564fc067ebddac69d8e8a80bc352294d758b31d13e6c168803f0edf6007b840fb864938af01a0d54ed66c99dd832480f57cbf93354d92dbe31fd406fba859e22d0b8c21ee8ad4357f8fcf75b97fad9e3616fce00d82425ab8ea7cc24b56a26eff6f6ca0f8f37eed160f45b06fb8a36fce6ad6d74bba4190b1a98f0cf95221281d936efbdbf97e1b85c57587a3c202109bd02b0ccac1c0865a52812ff25c6ced7a77eb02dc8b801760bceb81b5dd81497fa55c232c7492bc6317bcff50690bc531833b8f056eb38a2cd70a6593954e78d9f2a1cdfa67b0b906a32ff83bd1e5d632f37b869871b08381ce92e74ca749c5fddb7ae40ac0a0f51a01d2b5f7a67480c57b125fa84f186fe9f640b7461b9a0e35abb212f49cd209f27b1113fe34b0798d1c95cd6e28ebae33697debef7f295d707aefb198d9a0bc0e4b323afe706a0149918f17bc9ffefcb1523a0debc9a18cda5beba59ceca0ef0267a1b20842c607949f2257f3d70b75f00faf629ddcbdf935d0d0eda232b01ce76a7013707c66fbf745546f19d041fea89ec58d214f41dde90912477de87296d3bc7695f2a1b16e258c9525cabca394907434b9d2541d5b4ba692d0bb486213c2e5d7a4361343f0b069971fa22f76f36817ce137b60f0b5b4f0d1be40ce60e54f03a0b00effca458b4623092ca8a33bf27a0b8e9772316d687261523011c1e8187225b6ff80a03bfb97eabf1a873051ed965f503149a705b147d3007de670c317fcd6a7a516bd50c772891e2bb3315f6c2a87c3cb1119cddc826f543abc71957b0eac5663718ae339d51af5256268d9a0474a403a6ba3fc81d52d89f71bd2f831f8944aa85d6d3677e3cc4876aac91778000ccf21aa14ba977af5a4d10da02981669450d55161ca8f053a871d81b5106abdd77663f1ef78bbb0eea21e2a20aaa868967a5907ad5582638c573682a851040e436f2e6c9733ee4896a86784b2ca0f330604ba7a3c5b0b746ee42632eb4cf982d4f3c36ec1bdd34c8bdba023b0d20997d570bb040aab67abf15b560b7c04c52513aee47c6646503836ed35d6f16388eeeeeed374906d0dab5fd1dbf8e291cbec2a6927e9bc4c052c66a9870730b20ff69314b4f45b36eae7896c08e5a06e691c8449e48dedf97ddcd3d674f0b0887ebb0c7f788fa577d949c4de8991658cb0f8dc1af2af7f58aba794610ac002f87749f9d104ae27ff60d06695572ea506326c7e6bee213de54288e8bcae89c0e5751fde4c6228122ad8ff186fdf8bb495f2afbeb85a95ee5cec68db708475d117aca0d4cc27198e0fd43ab5d1f9d7ab9de2f1a36e9bf112589daff912cdc442ee6e8aec7409a15a5fe45e5c201e3a052f12e53d1a0e1ce8ad6607c91f8e9c503c1f9c286c2dde7f8d41abc15a6ebe3825163ede6981ccd6fc9b03112ec426a1fbf2b27f200f4adddcbf200880be7f5a73d27cd24d09e4c6e4d111fac582576079d882aecc30f178bcaa94f8eb567064c9ec9ecfa99034620d0406025400b7a25dc35566bb590e2b378dbaaaa71fcb2279e5b11c15b1a9c8b286b0d1e7d3d611a84b62c97b13a07825b337b9b57aaeef4f86253f4deb5674dcc7a909b0ba5a62bf59aa6a4c18e6a975776383169d14695d6b9a3d305e9a21e745b2b2839515c0daabcad6837b062b7d01dc1ba60505955ecb38d334a523d9b1dbb7ebcc565300e8ad930df265a9e0f95e724b5f713967a976ad3ff3430d23786c016722106840bc0c54b8ab48d4f17c802901bd97dceffa70035d35e38d5ff183830fe90caae070aad78800dfd19c788ef024bd49c9ea8160ac550d117d074afad668a4f6bd510ef8a176328fd8d356b31ca7ba711e3afe130b93dde15d246a37c1270b6e92202ddd3604ceeb469bf93dde8652ce468c184857e67d38139cd669a089aa6861e065c283d1803de3005a23494532da0146ed64b85e40b2de3b63ce5844b29b1d404103d4242b9372da3ab04383a671bf91156329c8325f09bca871eb9b2525bde1e641d847edfcd96cc86cdf5b50944b2faf2e9117ed17de6b5ddc68fcffd88c81a07df6735796c3d20ffd3a78ec7882bee8143ef052475edef9bed6517e3b52428923971ec747cf4f5b9b176d23a4ed0119f7a43044e08f766d367b5cd9ef5610ab73ad97dc6bf4d90f55efa1219facdcc830df23431f5629b286602d4ae5ef903199317dd1e0549502cb2eae16f20916a284e792e268e3b7a57b556ea3a42e9267e8565bbaf76819760ea487f2b6442945c0343be29ae94406f3bfc0df8fddc08ac82ba23e9335dc9f9747a73ad89f80083c64e4e0ca3c3ec5bac16c5c4b98829528096d11677e5b2add86d8d3cb2ab4820a27e6b200c99cb89696853ef6f65194da3ed8a69e1e02e7c956f04f25d64f6d43196af74edbcb57c753c75bad26b046355828fe4b56d8e7b094de4c5284acfb686fca6769822013917b3bf609de92f0cd0835181c4e1e9c37cde2a31e9b44c31b5b6404a6c21cb03c5e2ba6abe4e240fb2bfd29aee712736279e486f8a437aff7e469e85bd5897caad3b28a75e0f2577163a579747d38a674aee66657777f9eae6deb89fa31e82ff1f2694aa389b0d32280e5e10ca4652b0592d2f88f3f966aff1518c47d4f7c9de34cd5193199900117b78694a8c54fce6ee189a22d323e097b90b66f4e2f8012977f5ca861f02178255d84c899b963c3307fb1e41bf1f9200de68ac04bd3042eef9907817e6771daf9d3a34117c81a9c27021cfc836938d33259c9d272ad7d1e7a273b7b54c0a152b720fa2026885f1f12bf2656c147cd9c54fe4b76f6cda531400c8178ca9f2013c91ebee44600dfa4fafb44468f6b425e50c634a024094f8cca64359515839300d925b49ff62a8510bdfed6de3f229654068ce3d956f0f4773106f38d8d40908648c87ccd1c64a2d1e621164c253ff9472ccd8c804a718b313952b120e2488267507664201bc55514d572a0c99e79068801972bc67b062ffaf6db639d590000bc5b218af736380dfb85df08d376733e5f09f473bb3cf82902a0b1070756502076279e5874329df3255ecafb485a0df06e08147fe1f3f85ba6070df13f7552e109ce9a119487805b843c229b96ec320193763785454fef9c984ead471d8a0400a44f3eff7fced4690b673718372c28a8b6d6924d2d0ffcb7cd85d77ae1d7e542da333e9ebc4a6d3d8e4a18172c4a282c170024b8be7a889a57fcc75c1486d7d1b942c027af9c7f98beec95bae62e76068b236387c0f10d19c4afee1274960cc29b78f66787e9624cc182ffe8553674830347e79f7beb68a8b33debca0791f242dd0ad1bdea9a27eb052b4a873a88e3bd0f3be38d6447938527ae035114eec551dda82ab01c2c8a87c78f8441448d17832161b30824d95a71b06fd78b051c04f1be9ad79e03364982723397fc744222f42a3f9e50a53f14d94cfe4decd554040033d5be4a7665044745238a59ac3bf71264854501f0ef4ee5f670690d814881c259967a6df24dc395e0ec1c7b9df807e2280a57705836b38ddc025f3ce8ce6b22c59fbff66af05881bada0c7b1db9a5ba9b601b487b49a9fa48678e51b413b471c1e72a1591e1277aeddbc21c9e887e8c96997880d93b2865a610c04f470dd771e8dd57f4d99b31007bde7601a1873837f2a88c0dd1b886c1ed9b7b411393e1800753e85451a0a3669c9f9a909035e6455bcf5d67a1bdfc4aa243a17c16d6ee6100c493ceea0130062e564d26ad2584491a0a7e215674f3ce0aa5b68f3e3f80f02fc04703bd2c4ef3f812bf23717525e2dca862c640765dd9cbb0a897166ede12c52f69ad514c2b963166d8a0c16223a50170f085c3cb1213897891a2a4d0d6f19ba1a0b8e7faded21a7be15188a512a1e6dd25c9bcc6e6a31ef31cad5c0cb8e1932d820675400dd30942d518df2005ae322f8f3190392739e0b2827b725ec9c2d6738d204989cd547ba1cb3bd37ababc597f124283ddc47a82fe8a4fa89c181082cbc4ac86b67bf74625314994d870426a846c6024026b841efb1b1758390e713f1ca562c70d4385dc5f424727419fbfa3c6632ce96cddef10b998450a8c6f601c4418da4d9e1e56c4eb86c8fff01aec4955f1ddd3fad61f249b6e1a74e0520226c7f49df1282612cfbaf389d6f9081269abad88c9915f8329c6f7f10cd30fe22326f40b9c864dff34ed6c32d8934e1d7a4f42a2cfefb8680b1ec3ce5c91aa90004db4643e5d9b5333344b28816904858056b89e854b880fe2ab9c421bbe47d2c8583c3567a93087fb8a72181db348cfc076505940124d9c48723976a84af7a24568430053eee947b3d1d420d1d3f63ff8e9d42e558ced36e2e5d4c70c47d9b28528cb14379044613b775464e86f94f62e29058c9869322648535ee9bcf8f0b12b1ea3acc204f519f48096e19da14a408266e0c75a2f25a47aff402f8205a562df0af8e6b8ccedb6f16128fb2d577d884101b679c954fa42f4ac6715205efac01ae7bd1f024c6132107293a1553365471c18c03ed9bbb3efaba7a9cdcf4afd7116da94d4d486381dae6a51c4554f36a25179d759f998fe4827fca97fdafcccb17f7c3b9b29db30c16573e4df5197879a1251d3a20015accb4d22271892b67442848049adca18dcbabff5331e409d702b9f4ce5dc8fc6efd52d0205a4516d9b40c64c134bcc23d7627d66edf1d4cd52ae985c7acc213a6686aa7fb98a4cdf9cf2d51b9e05b1ddd31dfc6697cc75ccd917f08a92c8ca8c5faf475f0a188ede9370adb00419489e89e97aba84aa6c77d951f624f9aa99955d56625553c3e7a14ac2f1a9afa3f34f815507ead2a646c9dd5a23fb9fad905ed087cd01ecafe4c903c22aff103f2a3ca614f98f8542b65547bf55a39abd637ebde2b6e437a98f54fb517ce908abac19a16771f8e4ba0887e1b3597b168235be0d6c60d3317f9f792e60863b4060fca1d569252f592e4be383d4a6e86310499886aba36263aaa661f701666c210f6b18ba1d8923a713b639129aa452867e0a8cdc1ae0018714ac7e5890ce6651f7545b2d02d5786a59352cf669926d36a6e3f754a84edfd343d7f88dd270c86f6a7c997e90366731500e3fb282d8c87e1c14443fe2ec4443c7efcc51f1a7f9bf1ffa1be301c4fc73d6bbf6a01b5340fa3d004fde4da36228cc8018b8e27eacdb17bbdd7264a4afc92d63c6bfe9efde14b06ea85a69d6b3100ad8021fe0775f8e3026bdc4607fb045cb447f79eac7d124c3c42a094f9f606e54b2d35f52f1c9001bb4bd8ababc8b16501d01ad354ecc264cab305e988705243fb7966552bbdaf95b7047e6266edc6b12e49e88ef2ff364122afeae596f9ce91bd35073214211c436997a4a33d6fef5e70337e672d8381a93f5b4ea1d06c51c21cf2096e1c41634d00c90b794087fb215c836e4a64055cbb9d50de11ecfd6a1c3060663b2b2e216d082dce2579c3994ead2afe49907335ddc49d4fa813729246dce975da1681aa5fde9249e2c1c140de81649f13dbb6e19ae3a9276ea9d0a08e1750309b0b5fb997116c1e20f25234b03c264b71c4a071bbe93b6d0a69e96b3b8edcea6018758a2c7c350a89e5428f07ef709b5bbd06df80ae113dbde9e61c9dbc99f9022595848e4f0adc98fb3c5d9e954a6ebb28eea6929974874e43277ed5837eac650cb979f976b878063d0221e6cb4822bbe42daedf1076495c945aa7144ab839c9010f5803e219631b6d3ec25d9b21c36d1c2a68efb4d8988bc82e44666690f1e8141f3fe11884f2331e475ba32ab2828b967bb83b575028e4d557953ee3b31ddc20cfccfd83d09af4f819ec755b026d64700d8fbff93d76356cf1aceaca758b3c0938ec673153913cd1287aa0ddb0f8c55cebcb8b6bd95e693e32697fd45f0d7f1d346b773dbfa16ba8917577a6db93867c5dfe647fc231a750f07426e89c4f3d200eaf6e0111401ed6742c6aad55fa225545fd321c94f5edc7feb241fe50fae82fb8cc5f9868d4c5c036e2cda9da4e93057ad89a8b90102b0c9ea0ac2eb9d5501113836c665cca9810f9dca35f399ab192dcbb0e7519c8e948014873cf2ca61705d8ff4fe49045287dbabf5332675a57e14ba2810f3e537644190b9fe8dd2d3120d5b7f4fa873c1b794cf3eb623d39e3a81d8f20115bc06dc33bb3eb7e353f242815891e9dcc9c791ffc59e065f8644e2de3f017d563a9b890905bebe871ff65059ecbf4d0d89724072e1f589411113ccc8d23ca44a1fb4025fc2e668a46b8ee14499b147e6396c868861d8005d999377cbda38057848aff4f8cfc8e8dede9dc28fb01a73e4dfde3e362611ddaec86726e945e330c5c20a151ea891fe74467b60d80010d0eb3028bfeee573db93dc2df313c89c1e545cee7af941eda5eb8d76b0d7aa7561c776b8398ea77ea3611f65571284075419c9775b05ae1c25cc9c5270c3f7757b225fbd1bbe5a0d8a96f0a5712ddbf0e5b143e1d4792317599f42bad0316fd73d3a81d365df0237c745529a2dcedc0d7b8f02501807d36936d8a54d42f1757d6ddfd411af39155b234e2ca522cd89a0759d0c9b3009c21e43401e27e22b3bcdfac5a4bf8ee81b1a1089eb4a643126ba01e0a5ee69879e50293f1a85317111cc292cbbaec3354d0863b0707b0cf8da5ca077515b36f5f38e87e4fd70f0c4ab1f4726a7cbe45ef9f9c657aabba65cf75f300f8b159ba9445537e997d1c0a38797ccf19233d14cc678b564aaa62ba7d6fe01172087176fad6c022a03b790b771c9fdabe10966cab2c1f84d6306d5cf48bb0347d80648abb1cbe78018d9c23d23b20d882438917282c6f58e00ad0bdeeddcaa14e7d16afb4a43c54369156120df55e8d76925bc74e69051b0921dc7b0d875c31f39c25c4810020ee8e84c415772d10884a64cd58b1124838dc77ea531ff6a4713ff7df0a33ca14a524a1f526e18a39af4a43224fc7a5963aa752dfa3bc5404e3618c253c596d477babc8da2ba0faa41b3133f67892399ee860d6d5e8d9ee061e39abade8d637732d85f21517083241064b003dbc1841770c5d0bcd04fb2503548694c9d97633514ea2edf6105ca39c7d4b0f3d36fd793044be89b2fa706d58efa0bcb573cf9abe4d11f83f0c183013b5811ae5062ca3bc440b364998342c465f1668d4bbfaf12aa6f410392980d6dd86acf51dea00cba982be6df81d2b8b4cdadd98c3bfb9a91a771744250e604c7d24c359ab15538ae8245cda8ca13e343beb9a85573cd96828039985b30d246e7a5d651c10d38d4347fd971cd456cbc9e78092ebafe7df820a530f3a5c287b615013d1c779c150c58c5e31c09fac48cc02f8efab3734f8a832af325784074698dd42f5b194bd08879c34a6df579956489206b6164519d972288334d47611ea33bb49bc107f82c2345721e796f87f53d326e1ac09c4f18fcab1713ab76d0c7144690b06cb2dc939f5211d1d37a8ceca6f6525adf710fe16c845a4fc7257281b842c19810912bbb1181a95664dcf9cdba87933d18a5048fcfae7664ac7931fb028726b00de1f6167b66edf4b3f8c745d12ab05fdaba8ca21ca7af9da2ec00659d049dde97aa49977a67c603f4c2bab7438f0bae36297c1d9aa9e896ccb361a60e0636664c2e48de1a528a045b3a01560b636d654a39e0dbe256b47723048261f4a59ce977e1e501672a0cec140c227e4aa4ede24653dcc45c5e388676a36072c7fb30172fe6f20ba8947997fac2c3696b3ba00a0d5a72ffcd5303b757baa093f434459a7ba88638f93c652f21efecf51ac4bd77fc06754036ced23b37fed22cef64f7cd5bfd1e0dce9c8a5da6d2974277eeb9c58a9dc3b3c6377f306114421d1197fc743dc2702a514ef2005ed4b34827ca67956924d3dcc86b7d9b13d340a2d78116c10b1bf75ddd470444bb558272220216fff391bf80e073927da0f0528c0154b1ead201809b2fbb9e90d21130e0de106672c7ac9b9258ee31b0ed20e1d2a3ad2f14524dc6dbd6519ad7669c43718baeb840b4df0d60b63ef29e6e9dd2a2592a2485b3d08d02c3c8404e44934216e34044a22759b8b3728f223218b5f295780f9cade7602d5ee2f0e082b8496b6ce04ba731f12b75cd0bdf676cf2af817639ccc9cd465e586fafb46aaa741e92e1261e6b717887d3c59b46614c6755012e169cc4747a8ad9b7362db7c3b79961d0866bfbf14d6d6183088e414c4b3f81761d6c503bb919c930b6fd5183919068902f359610f08722729e69e6688f3710f9782a1fcb7c9761bcabdd9797daab86a468f69163c49e59d931f3325cf2313194ef9805a4822a4426e999d895f9be09611877935c4de99732a2e1b995a04bf293c16f50dbe83edf3b65eb4f1ef97f388090f68a459cc0418b59f4aaade9b54115cd4c9cf9c7cb92fe3eaa5773905f71f9d1be656454b9291abb50c854777260b0765017ff0a27eb26860ece0b01d7c55ba4a8c228a82c9c5ca23e13926c5cc1a833344934d57ffd077d9071e17feea84956531aec7e05bb1dab9756ae3e54b2cac7af90c75f6375648c2f8b674bbd9b185e60de61a8a394b2bba945c779e890ca1867869363f0464d34287fa19894f703d397a8d6a458aeca9d7b4e58183d80a8b15dbb1a653c8682eb2f4b5b15a2b902e4a5dd9a7dd0781e66f354b81a10c2c6afc3f1ee3f06e4cd03706ec2acba94d3cb3bf6f922902a175e7e66c7e72d02e8826662e5e9db072f79431dd1512a7a736fa613a54bc5d0f48301bbe05138a07f996452e14dbba87e625c5c707453408d57a90b5e1c00b32d529124643903a1d630414afe6cbb4844f94564047eb9aa90e20e6e6e8e613dde4522a110711ef246c165a05721424b72ad332073bea9cd9fecd0c17399707dcafe6ed9bdc294a037e9338dc66c09b9bbe36985c5b021b32332dd917851e19901e49fe96834d851f697162287487b87c4551915ab4b151622c982acd7b0b9929d96c94229bb39b294f5b3cfb484e00e734c1f1cf554245e8ed206230380af73e79c4db31bfac732de6f61ef04cb748a47dbf487ed550657ff86e49bee29a65b1715a2a7e5d3ab32e1b1433ad80a8dea89f7575572699057956128d7e99718f00c6fa760e017f0d2ab1d11445f18995ddee563f4be7edbc55c00ff911054d40b1a55aadac71d64609546a0bf7e60d23278f4f271af9f104608d828ddf9eb57be964fc18f864767121d36926304da1c1a148a52ed35f5e1a3f6a51829d2e9229d46397235df4a5040cc43d240a26cc55434b407257e196ada5d493a715143f5cdea8530e1d45d1e92c391930f76ad6d19ec4ffe89a0bc977aa9ab20c15ecc1f20258e01a98876cf12347696e548b31dee516049b98cde66dfe19251ba1ff94020dabe324b200f0a7050a238845983e210c9e15e8f3ec7bf2e6f18725e4473eb3dbe7c9d3672183260b6a78bfc93809d22bef74d3565c69fd13b8681d6a11c06d73dff9664c1ebb58254edd9a42b57c50d0e2f7b95fc10c669079664ebd96a6f6b7f4d002a77ed57c04d5df10845ec831a420a1c982975d1a6429b32f76e6317ec8786511386b681e25cb4398b7c549a0c0d9a732eeb5c6764e4f87ac90416fcf22b3af1440b73eeb0bda7a31c39cfd50515ca746a465382099ad154f57486ff330476565c51fdaec116cd06f12135c0e45eb7e09b570a61f018e005b23cf44e6ff57866c9c8b41fc0345299c562c4736f442ae214374c27d57b305127384f6246e3e77da25cc699f1154465412f4241252c9de6a83d1d3d3dcccf0698509119b402e4ebc99549d431000adf755ab7bc2e91eab518f9a869cf90437100a41efea719c022f1112b0732d92aa49a2e0b61579304f87fd5f2efc52e7c44e7833d768b2235c7a2d8f9d2b29f45ae0e6a7e4310d26798a38a7eb97e32fc9dbcfba1f7e21bf9558f9710ac719295105d1f67e56210f5b92470665106668ef157bdec212cae3e9d3ff5db6380510092ba62e722d8e15b63dfc208f0da6da7b7b8c08f1fdb332457579997dbe1a55656fb14af1665618bd99835f2cebd09fa5fc1e2c6e7b1623306fa3cef8222118a484d042c7303a487143b51e3f0362149b02d18386da67b3f2408a8d54600334f79c6ddff8fe36174a01050d23ab2d4824a4a9d2a83a72e9d7a5913341ef209237b71535b7642d53bd9221acf76ea745cbb20214b6329b07d4ac15e330af12ca05b10c4ee3534cd732724babb196b5fb9d10c6dc91e1ca941bce024206b625cf7d37d944300b328be339a3e68f309e9b49208e5b128530b10c57804c7dbb2f53769ebf98ffa07ec1792feca939be56afa2d3c507f3548896b5c544a36b3f001543e006128d8660f2c0ddbc1449c8abb1df3b43d20f1a5025b9d702735f76085ed8438f7206db012caeaed7393e2f38baca305a21adf1627f34baf1c30f080b6dee62259851650721e019d7343938a92790bdd6254529c0f808458a9faeb22fd7647ee7f88ac42223461145a13beacf792f2c1732413522a19cd8912235b20392ab99fdfcbff0730a4f09266760360248046179586ffc6aac16fee4959ed42fcb2a42eae6708f5321b81cd2a6eba65b4e4bda95d492d29eb4cc91a973b94604aaeef27e8d4380fa4348bfad2a5d04ff0c8741a4c6580e70b68e2ba9e109be048200f2f679401340331225721cb55ae25bc76cee9a2e91ec0fbdc7c52121d619e6e8b6cd9b22dfd385235c5fa74641630665df948bbd625b2392b6ddcae6510e57f9b5571e434d164d58add1191e991fefa22ef88ddb9f5489bbd2384d72ca237a23f5763adca7ab06789342a05c3a6ab31726d9af0bfe9fdcd54c3018e8292f74f83314abc69e0c017972849c43a7befc13a798510bcd209b693392e6c55a28ee660e838515626c4e2508b5f957fe493efeabd2fb47a5d78d933e77f1d36a22d4c6b8aab3c7fb36e74d09e8ba6c523b1d700b66ce264c5b282967c63865e52320f520400b50d0ef2a08222620180d77baee87a88f3277f9f07521a15526a40ffe656543e75472c2ed85b40079ea7f9f5a256305c80af89e1bb4f01c8268d02f7ef74c73700ed2635aad1020b702196334d729bdd030e68a4d8f5a34c32ddd246078d8a5730262a24d821ee291398b6acdadee7435f04c5df0c63e76cae163194fce3f1bc643d6b2fb4343c5690da8dd0b5ed100622e81565ecdc4c087dbce2b40d61120db9c0a71d36d0b2fc435f1da021f09df04f8d36d769bb321ed0cf31e43aed2037954298ef427efa3510b0c3c206caf6547ebe2df41480c737356b706e24fcfa855fa8271a3d0c199b3ce7537b1dc19b9e187bf9751bd1d451ef42c088940be2dc8ac5d8593360a22dc754eb06ebf3c18c15976f7a370bd14131f01127253cc2a46aac7bf0060a1ec47d431a6328fa2954d56da83cee9f06e4668ec07be8bb72ad9814f1be714d239172334b189076d76c73a58eaa5184b686ebe8101a8c697fcf172d2fc00e5649329913813eabae5ecefa0adb5a8dc104ab1618c050924147e8813b32d6b2801294e148358c514b2b833f5e3967fc39e887532c43015a15850353650f0a78495124ffaec054c8adc76dd625e75c044f71200e0d713017ad21288895f478722e00ac38d4996d0a8f7fe171a795d19746ec017ebe31650700f4c76eedf63c9da0a8dfdd4b27fa8a699c93d2a448f7d06d9dc52c4930d050ecffce1b87fe2223283f247434ba2ebf193c7ed32333df5fadcde96619c2039c8f6e34b099aca91b8fa88c3f290d6dd09f106142bedf0ac29c1f8c056d22a6ce85f45df8c5ad17640a9f790bdf9bdd55324c366fd72f6d75ba9bfb0d1be288316eda51c89921428ce04183c87bb0a9244fa80f44f03b973351ad38ab3bd24e709d2c7de57471d47ac0df964339fa5a72efc3da90692468046f3f24ea0f705e2c59642e41084ce3b47ee6790d2b6bd4375231bcf180a0fcae273dfa23ade15de9325fa0b39a74136e0cffba7d78cb5e0bbf463b8e089e5e62de7f99e0dd31291daf49a2a338755fc5f0a1ce1c3a9f1002bae3b98cd20aeb5a8de38b42f412e05e532caec3a035c15f9ea3f8db06561ec62ce0c7c0c12422dea24b92134820cfa206a98dd13a1b0fcb0cea8e22e20a3f3c33ac959d9df686c6b551bb4b10211e79312775da1da6a4b2f100bd9ab53fbefaf3ae09315e7a73f7e1ae3e0090b0fa21c0e42a1df058480618724085e343a58da5e825852cabc240c940f14fddc0e99cac75b8d9a5c6758b55d6e4f9bac10532ebc34d243c09d684e8277b99cb32c1a2d59cdfcca7fcee82b75433c2bf0df101951bb97cb6d631528d1497d3777029f3dc8926abb3302b92c0a8f04eb79b16f945dd114b85fd7a944bab027454b1040c01850b5af972a3a996295ef6d617d4fba03a75390f548f465adca35d0362ad013e1cc6760e9d018c26eb2b9f9c0d6a8bb18bcff4407ebe1b1835b17fc822048485770506df4d49cf421671a025f2e3db015310776b35a82fd2f586b21bf2860ed1373a2eb05749addd1898d4bcde45aaa34d4c94dc73a44a9f5266b9e311864709503a3e58518ce96a2c3e8dfc1e447609ee2cf76b6ca203a495c2b0125151c01ce091cd9e331aea74be7bad8fe0138c94b033e12da7c2c19cc8ec55c99091fb9d4006bf121c31dede198a0fdb9aa08420fb9f46eac4c803db032ec7c7318d16a34197350ede6c0343e321d3e860afc6fcd66ced27d0c13142518f054761d884aed3762b43ecd50a59df25c236e708789b8378409951cf05a14ae2f8c301e86e10e9a2c75dac1a808064a70a783f1bd6bcd5d1762e588332d3db71ad77d2c66895f66e4678a867b72d8fa053c51ebea627870628968d693351d2afffd301784ef0093bad30c1ba4ffdbf325c685fd021d468b962ef6e3834b1b16fc1a8026866eb630df467b433bc5030d03e31d1845fab0e2aad4e93678cae3b237e8751e0f33f6eeb7edfeecb02303f28ce4cb8fac0746f818f7b97f80284d2981894116758388812c470d1c522502beb11a2baa91bd7b9259977e6bb71cf538d0ba1f080cb65252c017f7d915cf72e1305cd9768c9cbc233d7a2cbac65601dd702b9b13f84d42fad54b4918a79b6f9b87317a1c84d61bdc347d2b2a707baa44e4727e00fd30dc95e3ffb7627e96bc745a782d686a6590d708e6b424ad1c38d32d78030685bf5d3407264c14f0f86a8db1bfd48a0ef5cc30565ec4af80f653686ebae92ccdc6a6e56fb0ecb116fe6bdb5e9b5df8def29e4d13ba2b4e1c43df8ac1ea591f708744d587ec77aa0f74434a6c0775234b42d67a303f914b2488df06d3adfb21dde903a21536b62058f8fa4e09577da8dced3d6a86f8c9493b47afb9e521bc2e0377854af2ae69bb1af04d10a8faabdeb54259dcbda8e554725b9f33a07c432a3e24bb51b780207643253c9de986abfbae1d89d751584a90996100e543d1482ce8cf358632f600945281f97aa741f687462fa3a23c1c01cc686030b029ce280ca0d82cea2b1c47193e344a6e4da2f3a256652bc22ed410f68ce3dff1894f7a1ffdbef96a47fa72bd8f73a38a915c2b099e0b4ece88a14bc60433cb8e405b050b234ee3a351f3635b76310c77f8886d3281c1b7a50f3626a2cc0ea25994d76d00d28261d65c0843cdd25b5f51f0f4e5c522c4aa467a5776b22d942e3e1de4601229057f6cbc81db25945b80c9c7762a9417c5e27965c09a3721087a8831cbe90fb7d1586f76ea5e296da6406828b0fb40b3322182ac14691b31ec674b650f030ed585df07aca077101dbf75aeb7d8832888831d9362571d9ddd452025455cf92707127b8374b4f589739517b47f44ed47c06f09bfa08e7d71e37cbbb95c9648300cbe5dc0454b493fc02b0e2babf80ac9ae3db7494f0cf7dc17e1e8ad12586001f75c172eac0f81ab8c4c2a69694dc9ca9442ee08444baf13bc42d3a8a9f1ed258b34cec704010c9cf981c7788d281f40d8992b1a2fd99509346da4e18068df280916a9553dd08cf6afac441b574aa35b74fc70521feb1044e4ff0651536c0f081ef95bf341fff600916e297ca6f383dbab078e848ac0dfdcf5a8857477995105d766a9d21236c4b06ba90d461862696361346329ca90b3a5e4fb045e6e0853277376295c715272fe5a774c7282a90b75068993786cd6fb6f11f671f10502ad01876b03a95d256fbe9d0b2ca9458e061b115c572c4beb67cf15bd0db54a1b9f2998435d14c36ed021240accc567f42705d1d96841da9e4aa166b30be259771a1eaea35bdac859a94e677251349fae7455a5df632c235b0d25e53e019da6ae7100171dab09430d4db4986c2716a8626194be4b67df0d4070e6a007acde0809f9012832959dad3f231e74c2dd4d3197830da3a0e2c48073e5cca2d41b5af904dd19078c02e4f2b4a2d0ab7f080b92fb061fd1cda443d33e9761d04b49b976141015c3d2e7c4e94871a724b8f24df4d4ab2688b3a0615bce95bcc6e919b1919ff3", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba010000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } From a1da6604d884ccdd8bb177a7bd829a21eb4546ac Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 17:12:28 +0200 Subject: [PATCH 16/54] apply phase b --- crates/aggregator/src/committee.rs | 68 +++++- crates/aggregator/src/ext.rs | 10 +- crates/aggregator/src/publickey_aggregator.rs | 148 ++++++++---- .../src/enclave_event/committee_finalized.rs | 30 +-- .../src/enclave_event/publickey_aggregated.rs | 8 + crates/evm/src/ciphernode_registry_sol.rs | 16 +- crates/evm/src/dkg_attestation_bundle.rs | 116 ++++++++++ crates/evm/src/lib.rs | 2 + crates/tests/tests/integration.rs | 2 + docs/pages/cryptography.mdx | 65 ++++-- docs/pages/internals/dkg.mdx | 91 +++++++- examples/CRISP/client/.env.example | 6 +- examples/CRISP/enclave.config.yaml | 12 +- examples/CRISP/server/.env.example | 3 +- examples/CRISP/test/crisp.spec.ts | 3 +- package.json | 1 + .../interfaces/ICiphernodeRegistry.sol | 37 ++- .../IDkgFoldAttestationVerifier.sol | 39 ++++ .../contracts/lib/CommitteeHashLib.sol | 18 +- .../contracts/lib/DkgFoldAttestationLib.sol | 80 +++++++ .../registry/CiphernodeRegistryOwnable.sol | 112 ++++++++- .../contracts/test/MockCiphernodeRegistry.sol | 30 +++ .../contracts/test/MockE3Program.sol | 19 +- .../verifiers/DkgFoldAttestationVerifier.sol | 215 ++++++++++++++++++ .../verifiers/bfv/BfvDecryptionVerifier.sol | 55 +++-- .../contracts/verifiers/bfv/BfvPkVerifier.sol | 47 ++-- packages/enclave-contracts/hardhat.config.ts | 3 +- .../modules/dkgFoldAttestationVerifier.ts | 11 + packages/enclave-contracts/package.json | 5 +- .../scripts/cleanIgnitionState.ts | 25 +- .../scripts/deployAndSave/bondingRegistry.ts | 8 +- .../ciphernodeRegistryOwnable.ts | 8 +- .../dkgFoldAttestationVerifier.ts | 49 ++++ .../deployAndSave/enclaveTicketToken.ts | 8 +- .../scripts/deployAndSave/enclaveToken.ts | 8 +- .../scripts/deployAndSave/mockPkVerifier.ts | 4 +- .../scripts/deployAndSave/mockProgram.ts | 4 +- .../scripts/deployAndSave/mockStableToken.ts | 8 +- .../scripts/deployAndSave/poseidonT3.ts | 4 +- .../scripts/deployAndSave/slashingManager.ts | 8 +- .../scripts/deployAndSave/verifiers.ts | 30 +-- .../scripts/deployEnclave.ts | 30 +++ .../scripts/runVerification.ts | 5 +- .../scripts/syncIntegrationConfig.ts | 30 +++ .../scripts/upgrade/bondingRegistry.ts | 4 +- .../upgrade/ciphernodeRegistryOwnable.ts | 4 +- .../scripts/upgrade/enclave.ts | 4 +- packages/enclave-contracts/scripts/utils.ts | 64 ++++++ .../enclave-contracts/tasks/ciphernode.ts | 85 +++---- packages/enclave-contracts/tasks/enclave.ts | 1 + packages/enclave-contracts/tasks/program.ts | 75 +++++- packages/enclave-contracts/tasks/utils.ts | 4 +- .../test/BfvDecryptionVerifier.spec.ts | 101 ++++---- .../test/BfvPkVerifier.spec.ts | 157 +++++++------ .../test/E3Lifecycle/E3Integration.spec.ts | 20 +- .../enclave-contracts/test/Enclave.spec.ts | 134 ++++++++++- .../test/Pricing/Pricing.spec.ts | 2 +- .../CiphernodeRegistryOwnable.spec.ts | 62 ++--- .../test/Slashing/CommitteeExpulsion.spec.ts | 8 +- templates/default/enclave.config.yaml | 14 +- tests/integration/base.sh | 59 +++-- tests/integration/enclave.config.yaml | 15 +- tests/integration/fns.sh | 6 +- tests/integration/lib/prebuild.sh | 44 +++- tests/integration/persist.sh | 13 +- tests/integration/test.sh | 54 ++++- 66 files changed, 1956 insertions(+), 455 deletions(-) create mode 100644 crates/evm/src/dkg_attestation_bundle.rs create mode 100644 packages/enclave-contracts/contracts/interfaces/IDkgFoldAttestationVerifier.sol create mode 100644 packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol create mode 100644 packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol create mode 100644 packages/enclave-contracts/ignition/modules/dkgFoldAttestationVerifier.ts create mode 100644 packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts create mode 100644 packages/enclave-contracts/scripts/syncIntegrationConfig.ts diff --git a/crates/aggregator/src/committee.rs b/crates/aggregator/src/committee.rs index f77289deb..4f92e0f8b 100644 --- a/crates/aggregator/src/committee.rs +++ b/crates/aggregator/src/committee.rs @@ -7,9 +7,12 @@ use alloy::primitives::Address; use anyhow::{anyhow, Result}; use e3_events::OrderedSet; +use std::collections::HashMap; use std::str::FromStr; -/// Parse ordered committee node strings (`topNodes` / `PublicKeyAggregated.nodes`) once at ingress. +/// Parse committee node strings from an [`OrderedSet`] (address-sorted iteration). +/// +/// Prefer [`committee_addresses_in_party_order`] for ZK / on-chain committee-hash binding. pub fn committee_addresses_from_nodes(nodes: &OrderedSet) -> Result> { nodes .iter() @@ -18,3 +21,66 @@ pub fn committee_addresses_from_nodes(nodes: &OrderedSet) -> Result, +) -> Result> { + party_ids + .iter() + .map(|party_id| { + let node = party_nodes + .get(party_id) + .ok_or_else(|| anyhow!("missing committee node for party_id {party_id}"))?; + Address::from_str(node) + .map_err(|e| anyhow!("invalid committee node address {node}: {e}")) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy::primitives::address; + use e3_utils::committee_hash::hash_committee_addresses; + + #[test] + fn party_order_differs_from_address_sorted_set() { + let party_nodes = HashMap::from([ + ( + 0u64, + "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc".to_string(), + ), + ( + 1u64, + "0x90F79bf6EB2c4f870365E785982E1f101E93b906".to_string(), + ), + ( + 2u64, + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65".to_string(), + ), + ]); + let party_ids = vec![0, 1, 2]; + + let party_order = committee_addresses_in_party_order(&party_ids, &party_nodes).unwrap(); + let mut nodes = OrderedSet::new(); + for node in party_nodes.values() { + nodes.insert(node.clone()); + } + let address_order = committee_addresses_from_nodes(&nodes).unwrap(); + + assert_ne!(party_order, address_order); + assert_eq!( + hash_committee_addresses(&party_order), + hash_committee_addresses(&[ + address!("0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc"), + address!("0x90F79bf6EB2c4f870365E785982E1f101E93b906"), + address!("0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65"), + ]) + ); + } +} diff --git a/crates/aggregator/src/ext.rs b/crates/aggregator/src/ext.rs index c2fdc5e1f..b1886f12d 100644 --- a/crates/aggregator/src/ext.rs +++ b/crates/aggregator/src/ext.rs @@ -186,6 +186,9 @@ fn load_committee_addresses(ctx: &E3Context, e3_id: &E3id) -> Result Result { let _ = ctx.set_dependency(COMMITTEE_ADDRESSES_KEY, addrs); } diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index 41a027494..d3e1da899 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::committee::committee_addresses_from_nodes; +use crate::committee::committee_addresses_in_party_order; use actix::prelude::*; use alloy::primitives::Address; use anyhow::{anyhow, bail, ensure, Context as _, Result}; @@ -15,8 +15,9 @@ use e3_events::{ DkgAggregationRequest, E3Failed, E3Stage, E3id, EnclaveEvent, EnclaveEventData, EventContext, FailureReason, KeyshareCreated, OrderedSet, PartyProofsToVerify, PkAggregationProofPending, PkAggregationProofRequest, PkAggregationProofSigned, Proof, ProofType, PublicKeyAggregated, - Seed, Sequenced, ShareVerificationComplete, ShareVerificationDispatched, SignedProofFailed, - SignedProofPayload, TypedEvent, VerificationKind, ZkRequest, ZkResponse, + Seed, Sequenced, ShareVerificationComplete, ShareVerificationDispatched, + SignedDkgFoldAttestation, SignedProofFailed, SignedProofPayload, TypedEvent, VerificationKind, + ZkRequest, ZkResponse, }; use e3_events::{trap, EType}; use e3_fhe::{Fhe, GetAggregatePublicKey}; @@ -120,6 +121,9 @@ pub enum PublicKeyAggregatorState { party_nodes: HashMap, /// DKG recursive proofs per party (restart-critical). dkg_node_proofs: HashMap>, + /// Per-party fold attestations collected with honest DKG folds. + #[serde(default)] + dkg_fold_attestations: HashMap, honest_party_ids: BTreeSet, dishonest_parties: BTreeSet, /// In-flight [`ZkRequest::DkgAggregation`], if any. @@ -133,6 +137,9 @@ pub enum PublicKeyAggregatorState { public_key: ArcBytes, keyshares: OrderedSet, nodes: OrderedSet, + /// Ascending `party_id` order (matches on-chain `topNodes` after finalize sort). + #[serde(default)] + committee_addresses: Vec
, }, } @@ -147,6 +154,16 @@ impl PublicKeyAggregatorState { } } + pub fn committee_addresses(&self) -> Option<&[Address]> { + match self { + PublicKeyAggregatorState::Complete { + committee_addresses, + .. + } if !committee_addresses.is_empty() => Some(committee_addresses.as_slice()), + _ => None, + } + } + pub fn init(threshold_n: usize, threshold_m: usize, seed: Seed) -> Self { PublicKeyAggregatorState::Collecting { threshold_n, @@ -518,6 +535,7 @@ impl PublicKeyAggregator { nodes: honest_nodes_set, party_nodes, dkg_node_proofs: HashMap::new(), + dkg_fold_attestations: HashMap::new(), honest_party_ids: honest_party_ids.clone(), dishonest_parties: dishonest_parties.clone(), dkg_aggregation_correlation: None, @@ -567,6 +585,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -582,6 +601,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -636,40 +656,51 @@ impl PublicKeyAggregator { ); return Ok(()); }; - let Some(ref proof) = msg.aggregated_proof else { - warn!( - party_id = msg.party_id, - "honest party reported DKG fold without proof — rejecting" - ); - return Ok(()); - }; - let Some(ref attestation) = msg.fold_attestation else { - warn!( - party_id = msg.party_id, - "DKG fold missing SignedDkgFoldAttestation — rejecting (attribution)" - ); - return Ok(()); - }; - let meta = self.params_preset.metadata(); - let committee_n = party_nodes.len(); - let committee_h = committee_n; - let n_moduli = meta.num_moduli as usize; - if let Err(e) = verify_dkg_fold_attestation( - &self.e3_id, - msg.party_id, - proof, - attestation, - expected_node, - committee_n, - committee_h, - n_moduli, - ) { - warn!( - party_id = msg.party_id, - error = %e, - "DKG fold attestation verification failed — rejecting" - ); - return Ok(()); + // Proof aggregation OFF: nodes emit `DKGRecursiveAggregationComplete` + // with `proof=None` and `attestation=None`. Accept it so + // `try_publish_complete` can detect `all_proofs_are_none` and publish. + // Proof aggregation ON: both must be present and verified together. + match (&msg.aggregated_proof, &msg.fold_attestation) { + (None, None) => { + // no-aggregation mode — skip attestation verification + } + (Some(proof), Some(attestation)) => { + let meta = self.params_preset.metadata(); + let committee_n = party_nodes.len(); + let committee_h = committee_n; + let n_moduli = meta.num_moduli as usize; + if let Err(e) = verify_dkg_fold_attestation( + &self.e3_id, + msg.party_id, + proof, + attestation, + expected_node, + committee_n, + committee_h, + n_moduli, + ) { + warn!( + party_id = msg.party_id, + error = %e, + "DKG fold attestation verification failed — rejecting" + ); + return Ok(()); + } + } + (Some(_), None) => { + warn!( + party_id = msg.party_id, + "DKG fold has proof but missing attestation — rejecting (attribution)" + ); + return Ok(()); + } + (None, Some(_)) => { + warn!( + party_id = msg.party_id, + "DKG fold has attestation but missing proof — rejecting" + ); + return Ok(()); + } } } @@ -686,6 +717,7 @@ impl PublicKeyAggregator { nodes, party_nodes, mut dkg_node_proofs, + mut dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -697,12 +729,16 @@ impl PublicKeyAggregator { return Ok(state); }; dkg_node_proofs.insert(msg.party_id, msg.aggregated_proof); + if let Some(attestation) = msg.fold_attestation.clone() { + dkg_fold_attestations.insert(msg.party_id, attestation); + } Ok(PublicKeyAggregatorState::GeneratingC5Proof { public_key, keyshare_bytes, nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -719,7 +755,7 @@ impl PublicKeyAggregator { fn try_dispatch_dkg_aggregation(&mut self, ec: &EventContext) -> Result<()> { let state = self.state.get(); let Some(PublicKeyAggregatorState::GeneratingC5Proof { - nodes, + party_nodes, dkg_node_proofs, honest_party_ids, c5_proof_pending, @@ -780,6 +816,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: _, @@ -797,6 +834,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: None, @@ -837,13 +875,12 @@ impl PublicKeyAggregator { return Ok(()); } - let committee_addresses = committee_addresses_from_nodes(&nodes)?; + let committee_addresses = committee_addresses_in_party_order(&party_ids, party_nodes)?; #[cfg(debug_assertions)] { - let n_registered = committee_addresses.len(); debug_assert_eq!( party_ids.len(), - n_registered, + committee_addresses.len(), "honest NodeFold count must equal registered committee size until expulsion enables H < N" ); } @@ -871,6 +908,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: _, @@ -887,6 +925,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: Some(corr), @@ -913,6 +952,9 @@ impl PublicKeyAggregator { let PublicKeyAggregatorState::GeneratingC5Proof { public_key, nodes, + party_nodes, + dkg_fold_attestations, + honest_party_ids, c5_proof_pending, dkg_aggregated_proof, dkg_aggregation_correlation: _, @@ -969,12 +1011,29 @@ impl PublicKeyAggregator { let pk_commitment = extract_pk_commitment(c5_proof)?; + let party_ids: Vec = honest_party_ids.iter().copied().collect(); + let committee_addresses = committee_addresses_in_party_order(&party_ids, &party_nodes)?; + + let dkg_attestation_bundle = match dkg_aggregated_proof.as_ref() { + Some(_) => { + let bundle = e3_evm::encode_dkg_attestation_bundle( + &honest_party_ids, + &party_nodes, + &dkg_fold_attestations, + )?; + Some(ArcBytes::from_bytes(&bundle)) + } + None => None, + }; + let event = PublicKeyAggregated { pubkey: public_key.clone(), e3_id: self.e3_id.clone(), nodes: nodes.clone(), + committee_addresses: committee_addresses.clone(), pk_commitment, dkg_aggregator_proof: dkg_aggregated_proof.clone(), + dkg_attestation_bundle, }; self.bus.publish(event, ec.clone())?; @@ -983,6 +1042,7 @@ impl PublicKeyAggregator { public_key, keyshares: OrderedSet::new(), nodes, + committee_addresses, }) })?; @@ -1012,6 +1072,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -1029,6 +1090,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation, @@ -1043,6 +1105,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: None, @@ -1096,6 +1159,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: _, @@ -1113,6 +1177,7 @@ impl PublicKeyAggregator { nodes, party_nodes, dkg_node_proofs, + dkg_fold_attestations, honest_party_ids, dishonest_parties, dkg_aggregation_correlation: None, @@ -1430,6 +1495,7 @@ mod tests { nodes: OrderedSet::new(), party_nodes: HashMap::new(), dkg_node_proofs: HashMap::new(), + dkg_fold_attestations: HashMap::new(), honest_party_ids: BTreeSet::new(), dishonest_parties: BTreeSet::new(), dkg_aggregation_correlation: Some(correlation_id), diff --git a/crates/events/src/enclave_event/committee_finalized.rs b/crates/events/src/enclave_event/committee_finalized.rs index 91b173613..a1c2e9b72 100644 --- a/crates/events/src/enclave_event/committee_finalized.rs +++ b/crates/events/src/enclave_event/committee_finalized.rs @@ -6,7 +6,6 @@ use crate::E3id; use actix::Message; -use alloy::primitives::U256; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; @@ -20,29 +19,22 @@ pub struct CommitteeFinalized { } impl CommitteeFinalized { - /// Sort committee members by ascending score so every node derives the same - /// deterministic ordering. The node with the lowest score ends up at index 0. - /// If scores are empty or unparseable, the order is left unchanged. + /// Sort committee members by ascending address so every node derives the same + /// deterministic ordering, matching the on-chain registry's canonical address-ascending + /// `topNodes` layout (see `_sortTopNodesByAscendingScore` in `CiphernodeRegistryOwnable`). + /// The node with the numerically lowest address ends up at index 0 (= party 0). + /// Address comparison is done in lowercase to be independent of EIP-55 checksumming. pub fn sort_by_score(&mut self) { - if self.scores.len() != self.committee.len() || self.scores.is_empty() { - return; - } - - // Build (index, parsed_score) pairs let mut indices: Vec = (0..self.committee.len()).collect(); - let parsed: Vec> = - self.scores.iter().map(|s| s.parse::().ok()).collect(); - - // If any score fails to parse, leave order unchanged - if parsed.iter().any(|s| s.is_none()) { - return; - } - - indices.sort_by_key(|&i| parsed[i].unwrap()); + indices.sort_by_key(|&i| self.committee[i].to_lowercase()); let sorted_committee: Vec = indices.iter().map(|&i| self.committee[i].clone()).collect(); - let sorted_scores: Vec = indices.iter().map(|&i| self.scores[i].clone()).collect(); + let sorted_scores: Vec = if self.scores.len() == self.committee.len() { + indices.iter().map(|&i| self.scores[i].clone()).collect() + } else { + self.scores.clone() + }; self.committee = sorted_committee; self.scores = sorted_scores; diff --git a/crates/events/src/enclave_event/publickey_aggregated.rs b/crates/events/src/enclave_event/publickey_aggregated.rs index 9c2d49ef8..aa3192372 100644 --- a/crates/events/src/enclave_event/publickey_aggregated.rs +++ b/crates/events/src/enclave_event/publickey_aggregated.rs @@ -6,6 +6,7 @@ use crate::{E3id, OrderedSet, Proof}; use actix::Message; +use alloy::primitives::Address; use derivative::Derivative; use e3_utils::ArcBytes; use serde::{Deserialize, Serialize}; @@ -19,6 +20,9 @@ pub struct PublicKeyAggregated { pub pubkey: ArcBytes, // TODO: ArcBytes ? pub e3_id: E3id, pub nodes: OrderedSet, + /// Committee addresses in ascending `party_id` (score) order for hash binding. + #[serde(default)] + pub committee_addresses: Vec
, /// Hash-based aggregated PK commitment (last public signal of the C5 proof). /// Passed as `pkCommitment` to `publishCommittee`. pub pk_commitment: [u8; 32], @@ -26,6 +30,10 @@ pub struct PublicKeyAggregated { /// for on-chain verification. `None` when proof aggregation is disabled. #[serde(default)] pub dkg_aggregator_proof: Option, + /// ABI-encoded `(Attestation[], PartySlotBinding[])` for on-chain fold attestation verify. + /// Required when `dkg_aggregator_proof` is present; `None` otherwise. + #[serde(default)] + pub dkg_attestation_bundle: Option, } impl Display for PublicKeyAggregated { diff --git a/crates/evm/src/ciphernode_registry_sol.rs b/crates/evm/src/ciphernode_registry_sol.rs index 65f24ef6f..6775d8047 100644 --- a/crates/evm/src/ciphernode_registry_sol.rs +++ b/crates/evm/src/ciphernode_registry_sol.rs @@ -491,6 +491,7 @@ impl Handler Handler, + dkg_attestation_bundle: Option<&[u8]>, ) -> Result { let e3_id_u256: U256 = e3_id.try_into()?; let public_key_bytes = Bytes::from(public_key.extract_bytes()); @@ -682,12 +685,17 @@ pub async fn publish_committee_to_registry encode_zk_proof(p)?, None => Bytes::new(), }; + let attestation_bundle: Bytes = match dkg_attestation_bundle { + Some(b) => Bytes::copy_from_slice(b), + None => Bytes::new(), + }; // RPC may not have synced finalization yet send_tx_with_retry("publishCommittee", &["CommitteeNotFinalized"], || { let provider = provider.clone(); let public_key_bytes = public_key_bytes.clone(); let proof = proof.clone(); + let attestation_bundle = attestation_bundle.clone(); async move { info!("Calling: contract.publishCommittee(..)"); let from_address = provider.provider().default_signer_address(); @@ -698,7 +706,13 @@ pub async fn publish_committee_to_registry, + party_nodes: &HashMap, + attestations: &HashMap, +) -> Result { + let mut binding_sols = Vec::with_capacity(honest_party_ids.len()); + let mut attestation_sols = Vec::with_capacity(honest_party_ids.len()); + + for party_id in honest_party_ids { + let node = party_nodes + .get(party_id) + .with_context(|| format!("missing party_nodes entry for party {party_id}"))?; + let att = attestations + .get(party_id) + .with_context(|| format!("missing fold attestation for party {party_id}"))?; + + if att.payload.party_id != *party_id { + return Err(anyhow!( + "attestation party_id {} does not match binding {}", + att.payload.party_id, + party_id + )); + } + + binding_sols.push(PartySlotBindingSol { + partyId: U256::from(*party_id), + node: Address::from_str(node) + .with_context(|| format!("invalid committee node address {node}"))?, + }); + + attestation_sols.push(DkgFoldAttestationSol { + partyId: U256::from(att.payload.party_id), + skAggCommit: B256::from(att.payload.agg_commits.sk_agg_commit), + esmAggCommit: B256::from(att.payload.agg_commits.esm_agg_commit), + signature: att.signature.extract_bytes().into(), + }); + } + + Ok(Bytes::from( + (attestation_sols, binding_sols).abi_encode_params(), + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy::primitives::keccak256; + use alloy::signers::{local::PrivateKeySigner, SignerSync}; + use e3_events::{DkgFoldAggCommits, DkgFoldAttestationPayload, E3id}; + + #[test] + fn roundtrip_abi_layout() { + let signer: PrivateKeySigner = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + .parse() + .unwrap(); + let payload = DkgFoldAttestationPayload { + e3_id: E3id::new("31337", 1), + party_id: 2, + agg_commits: DkgFoldAggCommits { + sk_agg_commit: [1u8; 32], + esm_agg_commit: [2u8; 32], + }, + }; + let signed = e3_events::SignedDkgFoldAttestation::sign(payload, &signer).unwrap(); + + let mut honest = BTreeSet::new(); + honest.insert(2); + let mut party_nodes = HashMap::new(); + party_nodes.insert(2, signer.address().to_string()); + let mut attestations = HashMap::new(); + attestations.insert(2, signed); + + let encoded = encode_dkg_attestation_bundle(&honest, &party_nodes, &attestations).unwrap(); + assert!(!encoded.is_empty()); + + let typehash = keccak256( + "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", + ); + assert_eq!(typehash.len(), 32); + } +} diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index d30c1b9a4..1509076b7 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -6,6 +6,7 @@ mod bonding_registry_sol; mod ciphernode_registry_sol; +mod dkg_attestation_bundle; mod enclave_sol_reader; mod enclave_sol_writer; pub mod error_decoder; @@ -27,6 +28,7 @@ pub use bonding_registry_sol::BondingRegistrySolReader; pub use ciphernode_registry_sol::{ CiphernodeRegistrySol, CiphernodeRegistrySolReader, CiphernodeRegistrySolWriter, }; +pub use dkg_attestation_bundle::encode_dkg_attestation_bundle; pub use enclave_sol_reader::EnclaveSolReader; pub use enclave_sol_writer::EnclaveSolWriter; pub use events::*; diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 1378265e9..b5ed5519b 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -2144,6 +2144,7 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { nodes: OrderedSet::from(eth_addrs.clone()), pk_commitment: actual_pk_commitment_1, dkg_aggregator_proof: None, + dkg_attestation_bundle: None, } .into() ); @@ -2185,6 +2186,7 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { nodes: OrderedSet::from(eth_addrs.clone()), pk_commitment: actual_pk_commitment_2, dkg_aggregator_proof: None, + dkg_attestation_bundle: None, } .into() ); diff --git a/docs/pages/cryptography.mdx b/docs/pages/cryptography.mdx index 1740456a1..4ad3006f6 100644 --- a/docs/pages/cryptography.mdx +++ b/docs/pages/cryptography.mdx @@ -196,9 +196,6 @@ keep different commitment kinds from being confused with one another. ### Proof aggregation -> **Note:** this section describes a proposed design that has not been implemented yet. It is -> included here as the target architecture for proof aggregation and on-chain verification. - Individual circuit proofs cover each step in isolation, but the protocol must land them on-chain as a compact, verifiable package. A naive fold of all proofs into one recursive aggregate produces an opaque commitment that the contract cannot interpret without the original public inputs, and if @@ -209,20 +206,58 @@ expected commitments match outputs) is only checked off-chain, end-to-end correctness still depends on trusting the committee network rather than on verifiable mathematics. -The aggregation design addresses both problems. Only **two recursive proofs** are posted on-chain -per E3 session: +The aggregation design addresses both problems. When an E3 is requested with +`proofAggregationEnabled = true`, only **two recursive proofs** are posted on-chain per E3 session: -1. **DKG recursive proof**: the aggregator recursively verifies every node's C0-C4 fold chain - together with **C5**, which aggregates the public key. +1. **DKG recursive proof**: the aggregator recursively verifies every node's C0–C4 fold chain + together with **C5**, which aggregates the public key. Verified on-chain by + [`BfvPkVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol) + at `publishCommittee`. 2. **Decryption recursive proof**: the aggregator recursively verifies **C6** proofs from at least - **T+1** parties together with **C7**, which reconstructs the plaintext. - -Nodes do not post proofs on-chain. Each node folds its own C0-C4 chain, **signs** the fold output, -and relays it to the aggregator via P2P gossip. The recursive proof verifies each signature inside -ZK, so the aggregator cannot substitute or fabricate any node's contribution without the proof -failing. During registration the contract stores the submitting address for each `party_id`; -verification recovers the signer via `ecrecover` and checks it matches, binding each fold output to -its creator. + **T+1** parties together with **C7**, which reconstructs the plaintext. Verified on-chain by + [`BfvDecryptionVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol) + at `publishPlaintextOutput`. + +Individual nodes do not post their inner proofs on-chain. Each node folds its own C0–C4 chain into a +`NodeDkgFold` proof, **signs** the surfaced commitments, and relays the signed fold output to the +aggregator via P2P gossip. The aggregator then assembles the recursive DKG proof from all honest +nodes' fold outputs and submits it together with a **fold-attestation bundle**. + +#### Per-node attestations and topNodes binding + +The fold-attestation bundle is the on-chain link from "this aggregated proof was produced by the +right operators" to "this specific signature came from the registered operator for `partyId = i`". +At `publishCommittee` the registry has already finalised `topNodes` (the address-sorted committee; +party 0 = lowest address, party 1 = next, ...), so the on-chain canonical mapping from a `partyId` +to an operator address is `topNodes[partyId]`. + +Each node's fold attestation signs an EIP-712-style digest over +`(chainId, e3Id, partyId, skAggCommit, esmAggCommit)` with the operator's registered key. The +on-chain +[`DkgFoldAttestationVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol) +then enforces three properties per attestation: + +1. **Signer = registered operator**: `ecrecover` over the signed digest must equal the `node` + address bound to that `partyId` in the bundle, and that address must be an active committee + member for this E3 (`isCommitteeMemberActive(e3Id, node)`). +2. **Commitments match the DKG proof**: the signed `skAggCommit` and `esmAggCommit` must equal the + `partyId`'s slot in the DKG aggregator proof's public inputs. +3. **Honest-set cardinality**: the bundle must contain exactly one attestation per honest party in + the proof, with `partyId` strictly ascending (no duplicates, no partial subsets). + +This closes the per-operator attribution gap: the aggregator cannot present a valid DKG proof +without also producing one signed attestation per honest party, each bound to the operator +registered at the corresponding `topNodes` slot. The recovered `partyIds`, `skAggCommits`, and +`esmAggCommits` are persisted as on-chain anchors (`dkgPartyIds`, `dkgSkAggCommits`, +`dkgEsmAggCommits`) so future decryption proofs can be checked against them. + +#### Committee hash binding + +The DKG proof's public inputs include a `committee_hash` split into 128-bit hi/lo limbs. +`CommitteeHashLib.hash` computes this as `keccak256` over the raw 20-byte addresses of `topNodes` +concatenated in address-ascending order (the same canonical order the off-chain Rust side uses, so +the hashes match byte-for-byte). This binds the proof to the exact committee finalised on-chain — no +other permutation of those addresses, and no other set of addresses, would produce a matching hash. #### Surfaced anchor values diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index 128629f36..9b6adc25b 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -258,7 +258,7 @@ ciphernode in **A**. ## P2: Public Key Aggregation (C5) -The aggregator (party_id=0) collects all public key shares from C1 and sums them: +The aggregator (the elected node for this E3) collects all public key shares from C1 and sums them: ``` pk_threshold = Σ pk_share_i for i ∈ A @@ -328,6 +328,95 @@ aggregator. --- +## Proof Aggregation and On-Chain Anchoring + +E3 programs can opt into **proof aggregation** by setting `proofAggregationEnabled = true` at +request time. In that mode the network does not post per-node, per-circuit proofs on chain. Instead +the aggregator submits two recursive proofs — one for DKG, one for decryption — together with +per-node attestations that bind each surfaced commitment to a specific registered operator. + +### Canonical party_id assignment + +`party_id` is each node's index in the committee's canonical ordering. On chain, the committee is +finalised as `topNodes`, sorted by **ascending address** (lowest numeric address = party 0). The +ciphernodes derive their own `party_id` from `position()` in the `CommitteeFinalized.committee` +array, which carries the same canonical order. Address-sort is deterministic and reproducible by +both sides from just the set of committee addresses, so the Rust and Solidity sides never need to +agree on per-run sortition scores to agree on the labeling. + +### Committee hash binding + +Both recursive proofs include a `committee_hash` in their public inputs, split into 128-bit +`committee_hash_hi` and `committee_hash_lo` for the BN254 field. The hash is `keccak256` over the +raw 20-byte addresses of `topNodes` concatenated in address-ascending order (see +`CommitteeHashLib.hash` in the registry and `e3_utils::committee_hash::hash_committee_addresses` in +Rust). The two implementations produce byte-identical output for the same committee, so the proof's +bound hash matches whatever the registry computed at `publishCommittee` — failing the verifier +otherwise. + +### NodeDkgFold and fold attestations + +After completing C0–C4 locally, each ciphernode folds its proofs into a single `NodeDkgFold` proof +that surfaces three values: `sk_agg_commit`, `esm_agg_commit`, and the node's `party_id`. The node +then signs an EIP-712-style digest over `(chainId, e3Id, partyId, skAggCommit, esmAggCommit)` with +its operator key and gossips the signed fold output to peers via `DKGRecursiveAggregationComplete`. + +In proof-aggregation mode, the aggregator collects all honest parties' fold outputs and: + +1. Recursively verifies each `NodeDkgFold` inside the DKG aggregator proof (so an aggregator cannot + fabricate a node's fold without the recursive proof failing). +2. Assembles a **DKG fold-attestation bundle**: one signed attestation per honest party plus a + `(partyId → node address)` binding table. + +The DKG aggregator proof surfaces, as public outputs, the array of `party_ids`, `sk_agg_commits[]`, +`esm_agg_commits[]`, the committee hash limbs, and the aggregated public-key commitment. + +### `publishCommittee` verification + +When the aggregator calls +[`CiphernodeRegistry.publishCommittee`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol), +the registry runs two checks in order: + +1. **Honk proof verification** via + [`BfvPkVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol). + It checks the recursive VK hashes for `node_fold` and `pk_aggregation` (so the underlying + circuits are the deployed ones), the committee-hash hi/lo limbs match + `CommitteeHashLib.hash(c.topNodes)`, the surfaced `pkCommitment` equals the caller-supplied + value, and the raw Honk proof verifies. +2. **Fold-attestation verification** via + [`DkgFoldAttestationVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol). + It walks the bundle's `(partyId, node)` bindings — strictly ascending by `partyId`, no duplicates + — and for each binding: requires `isCommitteeMemberActive(e3Id, node)`, recovers the signer of + the attestation via `ecrecover` against the EIP-191/712 digest, requires `signer == node`, and + checks that the signed `skAggCommit` / `esmAggCommit` match the DKG proof's surfaced commitments + at that party's slot. The honest-set cardinality must equal the honest-party count derived from + the proof's public-input shape (`(len - 6) / 3`). + +Only if both succeed does the registry store the per-party anchors: + +```solidity +dkgPartyIds[e3Id] = partyIds; +dkgSkAggCommits[e3Id] = skAgg; +dkgEsmAggCommits[e3Id] = esmAgg; +``` + +These anchors are what downstream decryption verifies against, and what slashing-by-attribution can +read to identify which registered operator produced which DKG output. The mapping +`operator = topNodes[partyIds[i]]` is a closed cryptographic chain: the proof binds to the addresses +(via committee hash), the attestation binds to the proof commitments and the operator key, and the +operator key was registered against `topNodes` at committee finalisation. + +### Decryption recursive proof + +The decryption recursive proof (`BfvDecryptionVerifier`) follows the same shape: a recursive Honk +proof aggregates **C6** from at least **T+1** parties plus **C7**, surfaces `expected_sk_commits[]` +and `expected_esm_commits[]` per party, and binds to the on-chain committee hash. The registry +compares the surfaced commitments against the anchors stored at `publishCommittee` — if any anchor +differs the proof is rejected — and the plaintext is decoded from the proof's last 100 public inputs +(one `uint64` coefficient each, packed into bytes). + +--- + ## Proof Lifecycle ### Generation diff --git a/examples/CRISP/client/.env.example b/examples/CRISP/client/.env.example index 34cd2a418..60e2dac4a 100644 --- a/examples/CRISP/client/.env.example +++ b/examples/CRISP/client/.env.example @@ -1,7 +1,7 @@ VITE_ENCLAVE_API=http://127.0.0.1:4000 VITE_WALLETCONNECT_PROJECT_ID= -# A token with a public mint function. Also used as the fee token for Enclave. -# This is its default address in Anvil node -VITE_CRISP_TOKEN=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 +# Voting eligibility token (MockVotingToken). Set after CRISP deploy — see deploy output / server .env. +# Enclave fee token (MockUSDC) is separate: packages/enclave-contracts/deployed_contracts.json → MockUSDC. +VITE_CRISP_TOKEN=0x9d4454B023096f34B160D6B654540c56A1F81688 # Addresses of requesters for which to show rounds in the UI. Comma separated Ethereum addresses. VITE_E3_REQUESTERS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266,0x70997970C51812dc3A010C7d01b50e0d17dc79C8 diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index ccc6c0d30..7757b9f9e 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -1,3 +1,5 @@ +# localhost: sync Enclave stack from packages/enclave-contracts/deployed_contracts.json after pnpm evm:deploy. +# e3_program (CRISPProgram): updated by `USE_MOCKS=true pnpm deploy:contracts` in packages/crisp-contracts. chains: - name: localhost rpc_url: ws://localhost:8545 @@ -7,19 +9,19 @@ chains: deploy_block: 37 enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 13 + deploy_block: 17 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 9 + deploy_block: 13 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 10 + deploy_block: 14 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 8 + deploy_block: 12 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 4 + deploy_block: 8 - name: "sepolia" enabled: false # Public Sepolia WebSocket endpoint (see repo docs for the recommended default). diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index ce927ce22..407f82e11 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -12,7 +12,8 @@ ETHERSCAN_API_KEY="" # Cron-job API key to trigger new rounds CRON_API_KEY=1234567890 -# Based on default Anvil deployments (mock Enclave stack + CRISPProgram) +# Enclave stack: sync from packages/enclave-contracts/deployed_contracts.json (localhost) after pnpm evm:deploy. +# E3_PROGRAM_ADDRESS (CRISPProgram): set by `cd packages/crisp-contracts && USE_MOCKS=true PRINT_ENV_VARS=true pnpm deploy:contracts` ENCLAVE_ADDRESS=0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e FEE_TOKEN_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 E3_PROGRAM_ADDRESS=0x0E801D84Fa97b50751Dbf25036d067dCf18858bF diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index 9a0ac312c..a52c685ce 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -20,7 +20,8 @@ const OUTPUT_DECRYPTION_WAIT = 80_000 // A small buffer for decryption async function runCliInit(): Promise { try { // Execute the command and wait for it to complete - const output = execSync('pnpm cli init --token-address 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 --balance-threshold 1000', { + const tokenAddress = process.env.VITE_CRISP_TOKEN ?? '0x9d4454B023096f34B160D6B654540c56A1F81688' + const output = execSync(`pnpm cli init --token-address ${tokenAddress} --balance-threshold 1000`, { encoding: 'utf-8', }) console.log('Command output:', output) diff --git a/package.json b/package.json index bf1be5b1f..ce19788ac 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "committee:publish": "cd packages/enclave-contracts && pnpm hardhat committee:publish", "e3:activate": "cd packages/enclave-contracts && pnpm e3:activate", "e3-program:publishInput": "cd packages/enclave-contracts && pnpm hardhat e3-program:publishInput", + "e3-program:setMockEnclave": "cd packages/enclave-contracts && pnpm hardhat e3-program:setMockEnclave", "e3:publishCiphertext": "cd packages/enclave-contracts && pnpm hardhat e3:publishCiphertext", "e3:get-plaintext": "cd packages/enclave-contracts && pnpm e3:get-plaintext", "evm:install": "cd packages/enclave-contracts && pnpm install", diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index 310b1b508..824ec22f6 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -220,6 +220,21 @@ interface ICiphernodeRegistry { /// @notice Supplied DKG aggregator proof failed verification error InvalidDkgProof(); + /// @notice Proof aggregation enabled but no fold attestation bundle was supplied + error FoldAttestationsRequired(); + + /// @notice `dkgFoldAttestationVerifier` is not configured on the registry + error FoldAttestationVerifierNotSet(); + + /// @notice Fold attestation bundle failed signature or public-input binding checks + error InvalidFoldAttestation(); + + /// @notice `partyId` from an attestation does not appear in the DKG proof public inputs + error PartyIdNotInProof(); + + /// @notice Attestation count does not match bindings or proof honest-set size + error AttestationBindingCountMismatch(); + /// @notice Node has already submitted a ticket for this E3 error NodeAlreadySubmitted(); @@ -319,13 +334,33 @@ interface ICiphernodeRegistry { /// @param pkCommitment Hash-based aggregated PK commitment for the committee. /// @param proof DkgAggregator (EVM) proof ABI-encoded `(bytes rawProof, bytes32[] publicInputs)`, /// or empty bytes when proof aggregation is disabled. + /// @param dkgAttestationBundle ABI-encoded + /// `(DkgFoldAttestationLib.Attestation[] attestations, DkgFoldAttestationLib.PartySlotBinding[] bindings)`. + /// Required (non-empty) when proof aggregation is enabled; ignored otherwise. function publishCommittee( uint256 e3Id, bytes calldata publicKey, bytes32 pkCommitment, - bytes calldata proof + bytes calldata proof, + bytes calldata dkgAttestationBundle ) external; + /// @notice Returns DKG anchor commitments stored at publication (empty if not yet published). + /// @param e3Id ID of the E3 + /// @return partyIds Honest party ids (same order as stored sk/esm arrays) + /// @return skAggCommits Per-party secret-key aggregate commitments from NodeFold + /// @return esmAggCommits Per-party smudging-noise aggregate commitments from NodeFold + function getDkgAnchors( + uint256 e3Id + ) + external + view + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ); + /// @notice This function should be called by the Enclave contract to get the public key of a committee. /// @dev This function MUST revert if no committee has been requested for the given E3. /// @dev This function MUST revert if the committee has not yet published a public key. diff --git a/packages/enclave-contracts/contracts/interfaces/IDkgFoldAttestationVerifier.sol b/packages/enclave-contracts/contracts/interfaces/IDkgFoldAttestationVerifier.sol new file mode 100644 index 000000000..561880155 --- /dev/null +++ b/packages/enclave-contracts/contracts/interfaces/IDkgFoldAttestationVerifier.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +pragma solidity 0.8.28; + +/** + * @title IDkgFoldAttestationVerifier + * @notice Verifies per-node DKG fold ECDSA attestations against a DkgAggregator proof. + * @dev Invoked via external call from the registry so verification does not share its stack frame. + */ +interface IDkgFoldAttestationVerifier { + /// @notice Verify attestations and return anchor arrays indexed by proof party-id slot. + /// @param registry Ciphernode registry (`isCommitteeMemberActive` callback). + /// @param chainId EIP-712 chain id used in attestation digests. + /// @param e3Id E3 identifier. + /// @param proof ABI-encoded `(bytes rawProof, bytes32[] publicInputs)` from the aggregator. + /// @param dkgAttestationBundle ABI-encoded + /// `(Attestation[] attestations, PartySlotBinding[] bindings)`; bindings sorted by ascending `partyId`. + /// @return partyIds Honest party ids (proof slot order) + /// @return skAggCommits Per-party sk aggregate commitments + /// @return esmAggCommits Per-party esm aggregate commitments + function verify( + address registry, + uint256 chainId, + uint256 e3Id, + bytes calldata proof, + bytes calldata dkgAttestationBundle + ) + external + view + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ); +} diff --git a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol index 9d72a15f5..6a405ce2a 100644 --- a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol +++ b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol @@ -14,10 +14,22 @@ pragma solidity 0.8.28; library CommitteeHashLib { uint256 private constant _LO_MASK = (uint256(1) << 128) - 1; - /// @notice `keccak256(abi.encodePacked(nodes))` for the ordered on-chain committee. - /// @dev Callers pass `storage` arrays via implicit copy to this `memory` parameter. + /// @notice `keccak256(concat(20-byte addresses))` for the ordered on-chain committee. + /// @dev Must match `e3_utils::committee_hash::hash_committee_addresses`, which packs + /// each address as raw 20 bytes with no padding. NOTE: `abi.encodePacked(address[])` + /// pads each element to 32 bytes (left-padded), which does NOT match the off-chain + /// canonical encoding — so we build the 20*N byte buffer manually. function hash(address[] memory nodes) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(nodes)); + uint256 n = nodes.length; + bytes memory packed = new bytes(n * 20); + for (uint256 i = 0; i < n; ++i) { + bytes20 a = bytes20(nodes[i]); + uint256 offset = i * 20; + for (uint256 j = 0; j < 20; ++j) { + packed[offset + j] = a[j]; + } + } + return keccak256(packed); } /// @notice High 128 bits of a committee hash (Noir public input `committee_hash_hi`). diff --git a/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol b/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol new file mode 100644 index 000000000..6d765fdbb --- /dev/null +++ b/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +pragma solidity 0.8.28; + +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import { + MessageHashUtils +} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; + +/** + * @title DkgFoldAttestationLib + * @notice EIP-712-style digests for per-node DKG fold attestations. + * @dev Must stay aligned with `DkgFoldAttestationPayload::typehash()` in + * `crates/events/src/enclave_event/dkg_fold_attestation.rs`. + */ +library DkgFoldAttestationLib { + /// @dev `keccak256("DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)")` + bytes32 public constant TYPEHASH = + keccak256( + "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)" + ); + + /// @notice One node's signed fold output. + struct Attestation { + uint256 partyId; + bytes32 skAggCommit; + bytes32 esmAggCommit; + bytes signature; + } + + /// @notice Maps sortition `partyId` to the operator that produced the fold. + struct PartySlotBinding { + uint256 partyId; + address node; + } + + /// @notice `personal_sign` digest for a fold attestation (EIP-191 applied by callers). + function digest( + uint256 chainId, + uint256 e3Id, + uint256 partyId, + bytes32 skAggCommit, + bytes32 esmAggCommit + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + TYPEHASH, + chainId, + e3Id, + partyId, + skAggCommit, + esmAggCommit + ) + ); + } + + /// @notice Recover the signer for a fold attestation. + function recoverSigner( + uint256 chainId, + uint256 e3Id, + Attestation memory attestation + ) internal pure returns (address) { + bytes32 structHash = digest( + chainId, + e3Id, + attestation.partyId, + attestation.skAggCommit, + attestation.esmAggCommit + ); + bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash( + structHash + ); + return ECDSA.recover(ethSignedHash, attestation.signature); + } +} diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 4456b4d46..f34e2a89a 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -18,6 +18,9 @@ import { LazyIMTData } from "@zk-kit/lazy-imt.sol/InternalLazyIMT.sol"; import { CommitteeHashLib } from "../lib/CommitteeHashLib.sol"; +import { + IDkgFoldAttestationVerifier +} from "../interfaces/IDkgFoldAttestationVerifier.sol"; /** * @title CiphernodeRegistryOwnable @@ -85,6 +88,14 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { /// @notice Address of the slashing manager authorized to expel committee members ISlashingManager public slashingManager; + /// @notice Verifies per-node DKG fold attestations at publication (external contract). + IDkgFoldAttestationVerifier public dkgFoldAttestationVerifier; + + /// @notice DKG anchor commitments stored when the committee public key is published. + mapping(uint256 e3Id => uint256[]) internal dkgPartyIds; + mapping(uint256 e3Id => bytes32[]) internal dkgSkAggCommits; + mapping(uint256 e3Id => bytes32[]) internal dkgEsmAggCommits; + //////////////////////////////////////////////////////////// // // // Modifiers // @@ -198,7 +209,8 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { uint256 e3Id, bytes calldata publicKey, bytes32 pkCommitment, - bytes calldata proof + bytes calldata proof, + bytes calldata dkgAttestationBundle ) external { Committee storage c = committees[e3Id]; @@ -217,10 +229,13 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { E3 memory e3 = enclave.getE3(e3Id); if (e3.proofAggregationEnabled) { - require(proof.length > 0, DkgProofRequired()); - require( - e3.pkVerifier.verify(pkCommitment, committeeHash, proof), - InvalidDkgProof() + _verifyAndStoreDkgAnchors( + e3Id, + e3, + pkCommitment, + committeeHash, + proof, + dkgAttestationBundle ); } @@ -238,6 +253,50 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { ); } + function _verifyAndStoreDkgAnchors( + uint256 e3Id, + E3 memory e3, + bytes32 pkCommitment, + bytes32 committeeHash, + bytes calldata proof, + bytes calldata dkgAttestationBundle + ) internal { + require(proof.length > 0, DkgProofRequired()); + require( + e3.pkVerifier.verify(pkCommitment, committeeHash, proof), + InvalidDkgProof() + ); + require(dkgAttestationBundle.length > 0, FoldAttestationsRequired()); + require( + address(dkgFoldAttestationVerifier) != address(0), + FoldAttestationVerifierNotSet() + ); + + ( + uint256[] memory partyIds, + bytes32[] memory skAgg, + bytes32[] memory esmAgg + ) = dkgFoldAttestationVerifier.verify( + address(this), + block.chainid, + e3Id, + proof, + dkgAttestationBundle + ); + + dkgPartyIds[e3Id] = partyIds; + dkgSkAggCommits[e3Id] = skAgg; + dkgEsmAggCommits[e3Id] = esmAgg; + } + + /// @notice Sets the DKG fold attestation verifier (required when proof aggregation is enabled). + function setDkgFoldAttestationVerifier( + IDkgFoldAttestationVerifier verifier + ) external onlyOwner { + require(address(verifier) != address(0), ZeroAddress()); + dkgFoldAttestationVerifier = verifier; + } + /// @inheritdoc ICiphernodeRegistry function addCiphernode(address node) external onlyOwnerOrBondingVault { if (isEnabled(node)) { @@ -354,6 +413,8 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { return false; } + _sortTopNodesByAscendingScore(c); + c.stage = ICiphernodeRegistry.CommitteeStage.Finalized; c.activeCount = c.topNodes.length; @@ -484,6 +545,26 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { committeeHash = c.committeeHash; } + /// @inheritdoc ICiphernodeRegistry + function getDkgAnchors( + uint256 e3Id + ) + external + view + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ) + { + require(publicKeyHashes[e3Id] != bytes32(0), CommitteeNotPublished()); + return ( + dkgPartyIds[e3Id], + dkgSkAggCommits[e3Id], + dkgEsmAggCommits[e3Id] + ); + } + /// @notice Returns the current size of the ciphernode IMT /// @return Size of the IMT function treeSize() public view returns (uint256) { @@ -673,6 +754,27 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { require(ticketNumber <= availableTickets, InvalidTicketNumber()); } + /// @notice Sort `topNodes` by ascending address before committee finalization. + /// @dev Canonical address-ascending order so `CommitteeHashLib.hash(topNodes)` + /// matches what off-chain aggregators independently compute over the same + /// address set (Rust uses `BTreeSet` which iterates lexicographically, + /// equivalent to numeric address-ascending for hex-encoded addresses). + /// This also defines `party_id` = position in the address-sorted committee. + /// @param c Committee storage reference + function _sortTopNodesByAscendingScore(Committee storage c) internal { + uint256 len = c.topNodes.length; + for (uint256 i = 0; i < len; ++i) { + for (uint256 j = i + 1; j < len; ++j) { + address left = c.topNodes[i]; + address right = c.topNodes[j]; + if (right < left) { + c.topNodes[i] = right; + c.topNodes[j] = left; + } + } + } + } + /// @notice Inserts a node into the top-N list - Smallest scores /// @dev O(N) linear scan per insertion to find the worst score. For a committee of size N /// with S total submissions, total gas is O(N * S). With N=20 and S=1000, this is ~20K diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index d6c8bb6f8..fbbce3fee 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -70,6 +70,7 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { uint256, bytes calldata, bytes32, + bytes calldata, bytes calldata ) external pure {} // solhint-disable-line no-empty-blocks @@ -83,6 +84,20 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { return keccak256(abi.encodePacked(_committeeNodes[e3Id])); } + function getDkgAnchors( + uint256 + ) + external + pure + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ) + { + return (partyIds, skAggCommits, esmAggCommits); + } + function root() external pure returns (uint256) { return 0; } @@ -214,6 +229,7 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { uint256, bytes calldata, bytes32, + bytes calldata, bytes calldata ) external pure {} // solhint-disable-line no-empty-blocks @@ -228,6 +244,20 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { return bytes32(0); } + function getDkgAnchors( + uint256 + ) + external + pure + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ) + { + return (partyIds, skAggCommits, esmAggCommits); + } + function root() external pure returns (uint256) { return 0; } diff --git a/packages/enclave-contracts/contracts/test/MockE3Program.sol b/packages/enclave-contracts/contracts/test/MockE3Program.sol index 972298f22..6356d483e 100644 --- a/packages/enclave-contracts/contracts/test/MockE3Program.sol +++ b/packages/enclave-contracts/contracts/test/MockE3Program.sol @@ -6,6 +6,7 @@ pragma solidity 0.8.28; import { IE3Program } from "../interfaces/IE3Program.sol"; +import { IEnclave } from "../interfaces/IEnclave.sol"; contract MockE3Program is IE3Program { error InvalidParams(bytes e3ProgramParams, bytes computeProviderParams); @@ -14,8 +15,19 @@ contract MockE3Program is IE3Program { bytes32 public constant ENCRYPTION_SCHEME_ID = keccak256("fhe.rs:BFV"); + /// @notice Optional Enclave contract — when set, `publishInput` forwards + /// `data` to `enclave.publishCiphertextOutput`, which is what the integration + /// tests rely on to trigger the ciphernode decryption pipeline. A real E3 + /// program would aggregate user inputs off-chain into a single ciphertext; + /// the mock short-circuits that step by treating the input as the ciphertext. + IEnclave public enclave; + mapping(uint256 e3Id => bytes32 paramsHash) public paramsHashes; + function setEnclave(IEnclave _enclave) external { + enclave = _enclave; + } + function validate( uint256 e3Id, uint256, @@ -33,10 +45,15 @@ contract MockE3Program is IE3Program { return ENCRYPTION_SCHEME_ID; } - function publishInput(uint256, bytes memory data) external pure { + function publishInput(uint256 e3Id, bytes memory data) external { if (data.length == 3) { revert InvalidInput(); } + if (address(enclave) != address(0)) { + // Pass `data` as the proof too so `MockE3Program.verify` (which + // requires `proof.length > 0`) returns true. + enclave.publishCiphertextOutput(e3Id, data, data); + } } function verify( diff --git a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol new file mode 100644 index 000000000..1760af153 --- /dev/null +++ b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +pragma solidity 0.8.28; + +import { ICiphernodeRegistry } from "../interfaces/ICiphernodeRegistry.sol"; +import { + IDkgFoldAttestationVerifier +} from "../interfaces/IDkgFoldAttestationVerifier.sol"; +import { DkgFoldAttestationLib } from "../lib/DkgFoldAttestationLib.sol"; + +/** + * @title DkgFoldAttestationVerifier + * @notice Stateless verifier for DKG fold attestations at committee publication. + */ +contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { + struct BundleData { + bytes32[] publicInputs; + DkgFoldAttestationLib.Attestation[] attestations; + DkgFoldAttestationLib.PartySlotBinding[] bindings; + uint256 h; + } + + /// @inheritdoc IDkgFoldAttestationVerifier + function verify( + address registry, + uint256 chainId, + uint256 e3Id, + bytes calldata proof, + bytes calldata dkgAttestationBundle + ) + external + view + returns ( + uint256[] memory partyIds, + bytes32[] memory skAggCommits, + bytes32[] memory esmAggCommits + ) + { + BundleData memory data = _loadBundle(proof, dkgAttestationBundle); + return _fillAnchors(registry, chainId, e3Id, data); + } + + function _loadBundle( + bytes calldata proof, + bytes calldata dkgAttestationBundle + ) private pure returns (BundleData memory data) { + (, data.publicInputs) = abi.decode(proof, (bytes, bytes32[])); + data.h = _honestPartyCount(data.publicInputs); + (data.attestations, data.bindings) = abi.decode( + dkgAttestationBundle, + ( + DkgFoldAttestationLib.Attestation[], + DkgFoldAttestationLib.PartySlotBinding[] + ) + ); + if ( + data.attestations.length != data.bindings.length || + data.bindings.length != data.h + ) { + revert ICiphernodeRegistry.AttestationBindingCountMismatch(); + } + } + + function _fillAnchors( + address registry, + uint256 chainId, + uint256 e3Id, + BundleData memory data + ) + private + view + returns ( + uint256[] memory partyIdsOut, + bytes32[] memory skAggOut, + bytes32[] memory esmAggOut + ) + { + partyIdsOut = new uint256[](data.h); + skAggOut = new bytes32[](data.h); + esmAggOut = new bytes32[](data.h); + + for (uint256 i = 0; i < data.h; i++) { + _applyBinding( + registry, + chainId, + e3Id, + data, + i, + partyIdsOut, + skAggOut, + esmAggOut + ); + } + } + + function _applyBinding( + address registry, + uint256 chainId, + uint256 e3Id, + BundleData memory data, + uint256 i, + uint256[] memory partyIdsOut, + bytes32[] memory skAggOut, + bytes32[] memory esmAggOut + ) private view { + if (i > 0) { + require( + data.bindings[i].partyId > data.bindings[i - 1].partyId, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + } + + (uint256 slot, bytes32 sk, bytes32 esm) = _verifyBinding( + registry, + chainId, + e3Id, + data, + data.bindings[i] + ); + + partyIdsOut[slot] = data.bindings[i].partyId; + skAggOut[slot] = sk; + esmAggOut[slot] = esm; + } + + function _honestPartyCount( + bytes32[] memory publicInputs + ) private pure returns (uint256 h) { + require( + publicInputs.length >= 6 && (publicInputs.length - 6) % 3 == 0, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + h = (publicInputs.length - 6) / 3; + } + + function _verifyBinding( + address registry, + uint256 chainId, + uint256 e3Id, + BundleData memory data, + DkgFoldAttestationLib.PartySlotBinding memory binding + ) private view returns (uint256 slot, bytes32 skCommit, bytes32 esmCommit) { + require( + ICiphernodeRegistry(registry).isCommitteeMemberActive( + e3Id, + binding.node + ), + ICiphernodeRegistry.InvalidFoldAttestation() + ); + + DkgFoldAttestationLib.Attestation memory att = _findAttestation( + data.attestations, + binding.partyId + ); + + address signer = DkgFoldAttestationLib.recoverSigner( + chainId, + e3Id, + att + ); + require( + signer == binding.node, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + + uint256 partyIdOffset = 2; + uint256 skOffset = 5 + data.h; + uint256 esmOffset = 5 + (2 * data.h); + + slot = _partySlot( + data.publicInputs, + partyIdOffset, + data.h, + binding.partyId + ); + + require( + data.publicInputs[skOffset + slot] == att.skAggCommit && + data.publicInputs[esmOffset + slot] == att.esmAggCommit, + ICiphernodeRegistry.InvalidFoldAttestation() + ); + + return (slot, att.skAggCommit, att.esmAggCommit); + } + + function _findAttestation( + DkgFoldAttestationLib.Attestation[] memory attestations, + uint256 partyId + ) private pure returns (DkgFoldAttestationLib.Attestation memory att) { + for (uint256 j = 0; j < attestations.length; j++) { + if (attestations[j].partyId == partyId) { + return attestations[j]; + } + } + revert ICiphernodeRegistry.InvalidFoldAttestation(); + } + + function _partySlot( + bytes32[] memory publicInputs, + uint256 partyIdOffset, + uint256 h, + uint256 partyId + ) private pure returns (uint256 slot) { + for (uint256 k = 0; k < h; k++) { + if (uint256(publicInputs[partyIdOffset + k]) == partyId) { + return k; + } + } + revert ICiphernodeRegistry.PartyIdNotInProof(); + } +} diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index 9654d15d6..6202f30aa 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -24,6 +24,16 @@ import { CommitteeHashLib } from "../../lib/CommitteeHashLib.sol"; * indices 2 and 3; total public-input length is preset-dependent. */ contract BfvDecryptionVerifier is IDecryptionVerifier { + /// @dev Debug-mode errors that pinpoint which check in `verify` failed. + /// These replace the previous silent `return false` so callers (e.g. Enclave's + /// `require(verify(...), InvalidDecryptionProof())`) surface the specific failure. + error BadPublicInputsLen(uint256 actual, uint256 expected); + error BadC6FoldKeyHash(bytes32 actual, bytes32 expected); + error BadC7KeyHash(bytes32 actual, bytes32 expected); + error BadCommitteeHashHi(bytes32 actual, bytes32 expected); + error BadCommitteeHashLo(bytes32 actual, bytes32 expected); + error BadPlaintextHash(bytes32 actual, bytes32 expected); + /// @dev Message is always the last 100 public inputs (100 uint64 coeffs = 800 bytes plaintext). uint256 internal constant MESSAGE_COEFFS_COUNT = 100; @@ -97,36 +107,41 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { ); if (publicInputs.length != expectedPublicInputsLen) { - return false; + revert BadPublicInputsLen( + publicInputs.length, + expectedPublicInputsLen + ); } if (publicInputs[0] != expectedC6FoldKeyHash) { - return false; + revert BadC6FoldKeyHash(publicInputs[0], expectedC6FoldKeyHash); } if (publicInputs[1] != expectedC7KeyHash) { - return false; + revert BadC7KeyHash(publicInputs[1], expectedC7KeyHash); } - if ( - publicInputs[COMMITTEE_HASH_HI_IDX] != - CommitteeHashLib.hi(committeeHash) - ) { - return false; + bytes32 expectedHi = CommitteeHashLib.hi(committeeHash); + if (publicInputs[COMMITTEE_HASH_HI_IDX] != expectedHi) { + revert BadCommitteeHashHi( + publicInputs[COMMITTEE_HASH_HI_IDX], + expectedHi + ); } - if ( - publicInputs[COMMITTEE_HASH_LO_IDX] != - CommitteeHashLib.lo(committeeHash) - ) { - return false; + bytes32 expectedLo = CommitteeHashLib.lo(committeeHash); + if (publicInputs[COMMITTEE_HASH_LO_IDX] != expectedLo) { + revert BadCommitteeHashLo( + publicInputs[COMMITTEE_HASH_LO_IDX], + expectedLo + ); } - if (!_verifyPlaintextHash(publicInputs, plaintextOutputHash)) { - return false; + bytes32 actualPlaintextHash = _computePlaintextHash(publicInputs); + if (actualPlaintextHash != plaintextOutputHash) { + revert BadPlaintextHash(actualPlaintextHash, plaintextOutputHash); } return circuitVerifier.verify(rawProof, publicInputs); } - function _verifyPlaintextHash( - bytes32[] memory publicInputs, - bytes32 plaintextOutputHash - ) internal view returns (bool) { + function _computePlaintextHash( + bytes32[] memory publicInputs + ) internal view returns (bytes32) { uint256 offset = expectedPublicInputsLen - MESSAGE_COEFFS_COUNT; bytes memory plaintext = new bytes(MESSAGE_COEFFS_COUNT * 8); for (uint256 i = 0; i < MESSAGE_COEFFS_COUNT; i++) { @@ -135,6 +150,6 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { plaintext[i * 8 + j] = bytes1(uint8(coeff >> (j * 8))); } } - return keccak256(plaintext) == plaintextOutputHash; + return keccak256(plaintext); } } diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol index 40f5aac8b..f32d3c64f 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol @@ -21,6 +21,17 @@ import { CommitteeHashLib } from "../../lib/CommitteeHashLib.sol"; * compiled DkgAggregator honest-set size (`lib::configs::default::H`). */ contract BfvPkVerifier is IPkVerifier { + /// @dev Debug-mode errors that pinpoint which check in `verify` failed. + /// These replace the previous silent `return false` so callers (e.g. the + /// registry's `require(verify(...), InvalidDkgProof())`) surface the + /// specific failure selector instead of the generic `InvalidDkgProof`. + error BadPublicInputsLen(uint256 actual, uint256 expected); + error BadNodesFoldKeyHash(bytes32 actual, bytes32 expected); + error BadC5KeyHash(bytes32 actual, bytes32 expected); + error BadCommitteeHashHi(bytes32 actual, bytes32 expected); + error BadCommitteeHashLo(bytes32 actual, bytes32 expected); + error BadPkCommitment(bytes32 actual, bytes32 expected); + /// @dev `dkg_aggregator` return field count: `1 + H + H + 1` (key hash + two `H` arrays + pk commitment). uint256 internal constant DKG_RETURN_TAIL_LEN = 2; @@ -78,28 +89,36 @@ contract BfvPkVerifier is IPkVerifier { ); if (publicInputs.length != expectedPublicInputsLen) { - return false; + revert BadPublicInputsLen( + publicInputs.length, + expectedPublicInputsLen + ); } if (publicInputs[0] != expectedNodesFoldKeyHash) { - return false; + revert BadNodesFoldKeyHash( + publicInputs[0], + expectedNodesFoldKeyHash + ); } if (publicInputs[1] != expectedC5KeyHash) { - return false; + revert BadC5KeyHash(publicInputs[1], expectedC5KeyHash); } - if ( - publicInputs[committeeHashHiIdx] != - CommitteeHashLib.hi(committeeHash) - ) { - return false; + bytes32 expectedHi = CommitteeHashLib.hi(committeeHash); + if (publicInputs[committeeHashHiIdx] != expectedHi) { + revert BadCommitteeHashHi( + publicInputs[committeeHashHiIdx], + expectedHi + ); } - if ( - publicInputs[committeeHashLoIdx] != - CommitteeHashLib.lo(committeeHash) - ) { - return false; + bytes32 expectedLo = CommitteeHashLib.lo(committeeHash); + if (publicInputs[committeeHashLoIdx] != expectedLo) { + revert BadCommitteeHashLo( + publicInputs[committeeHashLoIdx], + expectedLo + ); } if (publicInputs[pkCommitmentIdx] != pkCommitment) { - return false; + revert BadPkCommitment(publicInputs[pkCommitmentIdx], pkCommitment); } return circuitVerifier.verify(rawProof, publicInputs); } diff --git a/packages/enclave-contracts/hardhat.config.ts b/packages/enclave-contracts/hardhat.config.ts index 549ffb0a6..9dfd1dcf4 100644 --- a/packages/enclave-contracts/hardhat.config.ts +++ b/packages/enclave-contracts/hardhat.config.ts @@ -29,7 +29,7 @@ import { publishPlaintext, requestCommittee, } from "./tasks/enclave"; -import { publishInput } from "./tasks/program"; +import { publishInput, setMockProgramEnclave } from "./tasks/program"; import { cleanDeploymentsTask } from "./tasks/utils"; dotenv.config(); @@ -102,6 +102,7 @@ const config: HardhatUserConfig = { publishCommittee, getPlaintextOutput, publishInput, + setMockProgramEnclave, enableE3, cleanDeploymentsTask, updateSubmissionWindow, diff --git a/packages/enclave-contracts/ignition/modules/dkgFoldAttestationVerifier.ts b/packages/enclave-contracts/ignition/modules/dkgFoldAttestationVerifier.ts new file mode 100644 index 000000000..5e29344b8 --- /dev/null +++ b/packages/enclave-contracts/ignition/modules/dkgFoldAttestationVerifier.ts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +export default buildModule("DkgFoldAttestationVerifier", (m) => { + const dkgFoldAttestationVerifier = m.contract("DkgFoldAttestationVerifier"); + return { dkgFoldAttestationVerifier }; +}) as any; diff --git a/packages/enclave-contracts/package.json b/packages/enclave-contracts/package.json index d0795e61c..7df1e9c98 100644 --- a/packages/enclave-contracts/package.json +++ b/packages/enclave-contracts/package.json @@ -158,7 +158,7 @@ "compile:ts": "tsc", "coverage": "pnpm test --coverage", "deploy": "hardhat run scripts/run.ts", - "deploy:mocks": "export DEPLOY_MOCKS=true && pnpm clean:deployments && pnpm run deploy", + "deploy:mocks": "export DEPLOY_MOCKS=true && pnpm clean:deployments && hardhat run scripts/run.ts --network localhost", "deploy:verifiers": "hardhat run scripts/deployVerifiers.ts", "upgrade:enclave": "hardhat run scripts/upgrade/enclave.ts", "upgrade:bondingRegistry": "hardhat run scripts/upgrade/bondingRegistry.ts", @@ -186,7 +186,8 @@ "prerelease": "pnpm clean && pnpm compile && pnpm typechain", "release": "pnpm publish", "verify:contracts": "hardhat run scripts/runVerification.ts", - "updateSubmissionWindow": "hardhat ciphernode:window" + "updateSubmissionWindow": "hardhat ciphernode:window", + "utils:sync-integration-config": "hardhat run scripts/syncIntegrationConfig.ts --network localhost" }, "dependencies": { "@openzeppelin/contracts-upgradeable": "^5.0.2", diff --git a/packages/enclave-contracts/scripts/cleanIgnitionState.ts b/packages/enclave-contracts/scripts/cleanIgnitionState.ts index e9995ab99..d2e130ace 100644 --- a/packages/enclave-contracts/scripts/cleanIgnitionState.ts +++ b/packages/enclave-contracts/scripts/cleanIgnitionState.ts @@ -3,8 +3,7 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import fs from "fs"; -import path from "path"; +import { cleanLocalDeployments } from "./utils"; /** * Cleans deployment records for a specific network from deployed_contracts.json @@ -12,26 +11,8 @@ import path from "path"; * @param networkName - The network name (e.g., "localhost", "hardhat") */ export const cleanDeploymentRecords = (networkName: string): void => { - const deploymentsFile = path.join(process.cwd(), "deployed_contracts.json"); - - if (!fs.existsSync(deploymentsFile)) { - return; - } - - try { - const deployments = JSON.parse(fs.readFileSync(deploymentsFile, "utf8")); - - if (deployments[networkName]) { - console.log( - `Cleaning deployment records for network '${networkName}'...`, - ); - delete deployments[networkName]; - fs.writeFileSync(deploymentsFile, JSON.stringify(deployments, null, 2)); - console.log(`Cleaned deployment records for '${networkName}'`); - } - } catch (error) { - console.warn("Failed to clean deployment records:", error); - } + cleanLocalDeployments(networkName); + console.log(`Cleaned deployment records for local network '${networkName}'`); }; /** diff --git a/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts b/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts index 771cb4698..964f3cbd2 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts @@ -10,7 +10,11 @@ import { BondingRegistry__factory as BondingRegistryFactory, } from "../../types"; import { getProxyAdmin, verifyProxyAdminOwner } from "../proxy"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveBondingRegistry function @@ -49,7 +53,7 @@ export const deployAndSaveBondingRegistry = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs("BondingRegistry", chain); diff --git a/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts index 746d29800..1c6dfede9 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts @@ -10,7 +10,11 @@ import { CiphernodeRegistryOwnable__factory as CiphernodeRegistryOwnableFactory, } from "../../types"; import { getProxyAdmin, verifyProxyAdminOwner } from "../proxy"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveCiphernodeRegistryOwnable function @@ -37,7 +41,7 @@ export const deployAndSaveCiphernodeRegistryOwnable = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs( "CiphernodeRegistryOwnable", diff --git a/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts new file mode 100644 index 000000000..1e4f91c07 --- /dev/null +++ b/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; + +import type { DkgFoldAttestationVerifier } from "../../types"; +import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; + +export const deployAndSaveDkgFoldAttestationVerifier = async ( + hre: HardhatRuntimeEnvironment, +): Promise<{ + dkgFoldAttestationVerifier: DkgFoldAttestationVerifier; +}> => { + const { ethers, networkName } = await hre.network.connect(); + const chain = networkName ?? "localhost"; + + const existing = readDeploymentArgs("DkgFoldAttestationVerifier", chain); + if (existing?.address) { + console.log( + ` DkgFoldAttestationVerifier already deployed at ${existing.address}`, + ); + const dkgFoldAttestationVerifier = (await ethers.getContractAt( + "DkgFoldAttestationVerifier", + existing.address, + )) as DkgFoldAttestationVerifier; + return { dkgFoldAttestationVerifier }; + } + + const dkgFoldAttestationVerifier = await ethers.deployContract( + "DkgFoldAttestationVerifier", + ); + await dkgFoldAttestationVerifier.waitForDeployment(); + const address = await dkgFoldAttestationVerifier.getAddress(); + const blockNumber = await ethers.provider.getBlockNumber(); + + storeDeploymentArgs( + { address, blockNumber }, + "DkgFoldAttestationVerifier", + chain, + ); + console.log(` DkgFoldAttestationVerifier deployed to: ${address}`); + + return { + dkgFoldAttestationVerifier: + dkgFoldAttestationVerifier as DkgFoldAttestationVerifier, + }; +}; diff --git a/packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts b/packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts index 36123181b..245692d70 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts @@ -9,7 +9,11 @@ import { EnclaveTicketToken, EnclaveTicketToken__factory as EnclaveTicketTokenFactory, } from "../../types"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveEnclaveTicketToken function @@ -36,7 +40,7 @@ export const deployAndSaveEnclaveTicketToken = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs("EnclaveTicketToken", chain); diff --git a/packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts b/packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts index d3ef2919d..34eefcff8 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts @@ -9,7 +9,11 @@ import { EnclaveToken, EnclaveToken__factory as EnclaveTokenFactory, } from "../../types"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveEnclaveToken function @@ -57,7 +61,7 @@ export const deployAndSaveEnclaveToken = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs("EnclaveToken", chain); diff --git a/packages/enclave-contracts/scripts/deployAndSave/mockPkVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/mockPkVerifier.ts index dc1a910fa..77a2cfa5f 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/mockPkVerifier.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/mockPkVerifier.ts @@ -9,7 +9,7 @@ import { MockPkVerifier, MockPkVerifier__factory as MockPkVerifierFactory, } from "../../types"; -import { storeDeploymentArgs } from "../utils"; +import { getDeploymentChain, storeDeploymentArgs } from "../utils"; export const deployAndSaveMockPkVerifier = async ( hre: HardhatRuntimeEnvironment, @@ -18,7 +18,7 @@ export const deployAndSaveMockPkVerifier = async ( }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); const pkVerifierFactory = await ethers.getContractFactory("MockPkVerifier"); const pkVerifier = await pkVerifierFactory.deploy(); diff --git a/packages/enclave-contracts/scripts/deployAndSave/mockProgram.ts b/packages/enclave-contracts/scripts/deployAndSave/mockProgram.ts index c6a78ee00..d7202c1fe 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/mockProgram.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/mockProgram.ts @@ -9,7 +9,7 @@ import { MockE3Program, MockE3Program__factory as MockE3ProgramFactory, } from "../../types"; -import { storeDeploymentArgs } from "../utils"; +import { getDeploymentChain, storeDeploymentArgs } from "../utils"; interface MockProgramArgs { hre: HardhatRuntimeEnvironment; @@ -22,7 +22,7 @@ export const deployAndSaveMockProgram = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); const e3ProgramFactory = await ethers.getContractFactory("MockE3Program"); const e3Program = await e3ProgramFactory.deploy(); diff --git a/packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts b/packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts index 539599a43..a4ad38e52 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts @@ -6,7 +6,11 @@ import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; import { MockUSDC, MockUSDC__factory as MockUSDCFactory } from "../../types"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveMockStableToken function @@ -29,7 +33,7 @@ export const deployAndSaveMockStableToken = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs("MockUSDC", chain); diff --git a/packages/enclave-contracts/scripts/deployAndSave/poseidonT3.ts b/packages/enclave-contracts/scripts/deployAndSave/poseidonT3.ts index b301480e2..6cdaa1032 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/poseidonT3.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/poseidonT3.ts @@ -6,7 +6,7 @@ import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; import poseidon from "poseidon-solidity"; -import { storeDeploymentArgs } from "../utils"; +import { getDeploymentChain, storeDeploymentArgs } from "../utils"; interface PoseidonT3ProxyDeployArgs { hre: HardhatRuntimeEnvironment; @@ -20,7 +20,7 @@ export const deployAndSavePoseidonT3 = async ({ hre, }: PoseidonT3ProxyDeployArgs): Promise => { const { ethers } = await hre.network.connect(); - const chain = hre.globalOptions.network; + const chain = getDeploymentChain(hre); // First check if the proxy exists if ((await ethers.provider.getCode(poseidon.proxy.address)) === "0x") { diff --git a/packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts b/packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts index 11292961b..6902506b4 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts @@ -9,7 +9,11 @@ import { SlashingManager, SlashingManager__factory as SlashingManagerFactory, } from "../../types"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; /** * The arguments for the deployAndSaveSlashingManager function @@ -32,7 +36,7 @@ export const deployAndSaveSlashingManager = async ({ }> => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); const preDeployedArgs = readDeploymentArgs("SlashingManager", chain); diff --git a/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts b/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts index 93be63459..cfb7c6f1b 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts @@ -3,13 +3,16 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import type { Provider } from "ethers"; import fs from "fs"; import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; import path from "path"; import { fileURLToPath } from "url"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; const BFV_HONK_VERIFIER_DIR = "contracts/verifiers/bfv/honk"; const NPM_HONK_SOURCE_PREFIX = @@ -18,25 +21,6 @@ const NPM_HONK_SOURCE_PREFIX = const NPM_HONK_LIBRARY_LINK_PREFIX = "npm/@enclave-e3/contracts@local/contracts/verifiers/bfv/honk"; -/** - * Deployment bucket key from the connected provider (avoids hre.globalOptions.network). - * Uses network.name when set and not "unknown"; otherwise chainId. - */ -const chainBucketKeyFromProvider = async ( - provider: Provider, -): Promise => { - try { - const network = await provider.getNetwork(); - const name = network.name?.trim(); - if (name && name !== "unknown") { - return name; - } - return `chainId:${network.chainId.toString()}`; - } catch { - return "localhost"; - } -}; - /** True when Hardhat artifacts use npm paths (consuming project like CRISP). */ const isNpmArtifactContext = (): boolean => !fs.existsSync(path.join(process.cwd(), BFV_HONK_VERIFIER_DIR)); @@ -132,7 +116,7 @@ export const deployAndSaveVerifier = async ( zkTranscriptLibAddress: string, ): Promise<{ address: string }> => { const { ethers } = await hre.network.connect(); - const chain = await chainBucketKeyFromProvider(ethers.provider); + const chain = getDeploymentChain(hre); // Check if already deployed const existing = readDeploymentArgs(contractName, chain); @@ -187,7 +171,7 @@ export const deployAndSaveAllVerifiers = async ( ): Promise => { const contractNames = discoverVerifierContracts(); const { ethers } = await hre.network.connect(); - const chain = await chainBucketKeyFromProvider(ethers.provider); + const chain = getDeploymentChain(hre); console.log(` Deploying to network: ${chain}`); if (contractNames.length === 0) { diff --git a/packages/enclave-contracts/scripts/deployEnclave.ts b/packages/enclave-contracts/scripts/deployEnclave.ts index f97f399be..ba73d0d8e 100644 --- a/packages/enclave-contracts/scripts/deployEnclave.ts +++ b/packages/enclave-contracts/scripts/deployEnclave.ts @@ -11,6 +11,7 @@ import { deployAndSaveBfvDecryptionVerifier } from "./deployAndSave/bfvDecryptio import { deployAndSaveBfvPkVerifier } from "./deployAndSave/bfvPkVerifier"; import { deployAndSaveBondingRegistry } from "./deployAndSave/bondingRegistry"; import { deployAndSaveCiphernodeRegistryOwnable } from "./deployAndSave/ciphernodeRegistryOwnable"; +import { deployAndSaveDkgFoldAttestationVerifier } from "./deployAndSave/dkgFoldAttestationVerifier"; import { deployAndSaveE3RefundManager } from "./deployAndSave/e3RefundManager"; import { deployAndSaveEnclave } from "./deployAndSave/enclave"; import { deployAndSaveEnclaveTicketToken } from "./deployAndSave/enclaveTicketToken"; @@ -128,6 +129,14 @@ export const deployEnclave = async ( const enclaveTokenAddress = await enclaveToken.getAddress(); console.log("EnclaveToken deployed to:", enclaveTokenAddress); + if (enclaveTokenAddress.toLowerCase() === feeTokenAddress.toLowerCase()) { + throw new Error( + "MockUSDC and EnclaveToken resolved to the same address. " + + "Start a fresh Anvil on http://127.0.0.1:8545 (e.g. `anvil --chain-id 31337`) " + + "and rerun deploy so token nonces advance separately.", + ); + } + console.log("Deploying EnclaveTicketToken..."); const { enclaveTicketToken } = await deployAndSaveEnclaveTicketToken({ baseToken: feeTokenAddress, @@ -390,6 +399,26 @@ export const deployEnclave = async ( } } + let dkgFoldAttestationVerifierAddress: string | undefined; + if (shouldHaveZKVerification) { + console.log("Deploying DkgFoldAttestationVerifier..."); + const { dkgFoldAttestationVerifier } = + await deployAndSaveDkgFoldAttestationVerifier(hre); + dkgFoldAttestationVerifierAddress = + await dkgFoldAttestationVerifier.getAddress(); + const currentVerifier = + await ciphernodeRegistry.dkgFoldAttestationVerifier(); + if (currentVerifier !== dkgFoldAttestationVerifierAddress) { + const tx = await ciphernodeRegistry.setDkgFoldAttestationVerifier( + dkgFoldAttestationVerifierAddress, + ); + await tx.wait(); + console.log( + "Successfully set DkgFoldAttestationVerifier on CiphernodeRegistry", + ); + } + } + const verifierLines = verifierEntries.length > 0 ? verifierEntries.map(([name, addr]) => ` ${name}: ${addr}`).join("\n") @@ -415,6 +444,7 @@ export const deployEnclave = async ( PkVerifier (BFV): ${pkVerifierAddress} Circuit Verifiers: ${verifierLines} + DkgFoldAttestationVerifier: ${dkgFoldAttestationVerifierAddress ?? "(not deployed)"} ============================================ `); }; diff --git a/packages/enclave-contracts/scripts/runVerification.ts b/packages/enclave-contracts/scripts/runVerification.ts index f1b7b2998..95036d0a7 100644 --- a/packages/enclave-contracts/scripts/runVerification.ts +++ b/packages/enclave-contracts/scripts/runVerification.ts @@ -5,12 +5,11 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import hre from "hardhat"; +import { getDeploymentChain } from "./utils"; import { verifyContracts } from "./verify"; async function main() { - const { ethers } = await hre.network.connect(); - const [signer] = await ethers.getSigners(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); verifyContracts(chain); } diff --git a/packages/enclave-contracts/scripts/syncIntegrationConfig.ts b/packages/enclave-contracts/scripts/syncIntegrationConfig.ts new file mode 100644 index 000000000..6968ae414 --- /dev/null +++ b/packages/enclave-contracts/scripts/syncIntegrationConfig.ts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import path from "path"; +import { fileURLToPath } from "url"; + +import { updateE3Config } from "./utils"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const contractMapping: Record = { + MockE3Program: "e3_program", + Enclave: "enclave", + CiphernodeRegistryOwnable: "ciphernode_registry", + BondingRegistry: "bonding_registry", + SlashingManager: "slashing_manager", + MockUSDC: "fee_token", +}; + +const integrationConfigPath = path.resolve( + __dirname, + "../../../tests/integration/enclave.config.yaml", +); + +const chain = process.env.HARDHAT_NETWORK ?? "localhost"; + +updateE3Config(chain, integrationConfigPath, contractMapping); diff --git a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts index de36fc2ff..cf14456bd 100644 --- a/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts +++ b/packages/enclave-contracts/scripts/upgrade/bondingRegistry.ts @@ -6,7 +6,7 @@ import hre from "hardhat"; import { upgradeAndSaveBondingRegistry } from "../deployAndSave/bondingRegistry"; -import { readDeploymentArgs } from "../utils"; +import { getDeploymentChain, readDeploymentArgs } from "../utils"; /** * Upgrades the BondingRegistry contract implementation and saves the deployment arguments @@ -16,7 +16,7 @@ export const upgradeBondingRegistry = async () => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); const signerAddress = await signer.getAddress(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); console.log("Signer:", signerAddress); const preDeployedArgs = readDeploymentArgs("BondingRegistry", chain); diff --git a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts index 32e095bb4..4b767cf1a 100644 --- a/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts +++ b/packages/enclave-contracts/scripts/upgrade/ciphernodeRegistryOwnable.ts @@ -7,7 +7,7 @@ import hre from "hardhat"; import { upgradeAndSaveCiphernodeRegistryOwnable } from "../deployAndSave/ciphernodeRegistryOwnable"; import { deployAndSavePoseidonT3 } from "../deployAndSave/poseidonT3"; -import { readDeploymentArgs } from "../utils"; +import { getDeploymentChain, readDeploymentArgs } from "../utils"; /** * Upgrades the CiphernodeRegistryOwnable contract implementation and saves the deployment arguments @@ -17,7 +17,7 @@ export const upgradeCiphernodeRegistryOwnable = async () => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); const signerAddress = await signer.getAddress(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); console.log("Signer:", signerAddress); const poseidonT3 = await deployAndSavePoseidonT3({ hre }); diff --git a/packages/enclave-contracts/scripts/upgrade/enclave.ts b/packages/enclave-contracts/scripts/upgrade/enclave.ts index 0e5fa3e2a..8ed56ef2c 100644 --- a/packages/enclave-contracts/scripts/upgrade/enclave.ts +++ b/packages/enclave-contracts/scripts/upgrade/enclave.ts @@ -6,7 +6,7 @@ import hre from "hardhat"; import { upgradeAndSaveEnclave } from "../deployAndSave/enclave"; -import { readDeploymentArgs } from "../utils"; +import { getDeploymentChain, readDeploymentArgs } from "../utils"; /** * Upgrades the Enclave contract implementation and saves the deployment arguments @@ -16,7 +16,7 @@ export const upgradeEnclave = async () => { const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); const signerAddress = await signer.getAddress(); - const chain = (await signer.provider?.getNetwork())?.name ?? "localhost"; + const chain = getDeploymentChain(hre); console.log("Signer:", signerAddress); const preDeployedArgs = readDeploymentArgs("Enclave", chain); diff --git a/packages/enclave-contracts/scripts/utils.ts b/packages/enclave-contracts/scripts/utils.ts index 33c05e8c8..eeb28c487 100644 --- a/packages/enclave-contracts/scripts/utils.ts +++ b/packages/enclave-contracts/scripts/utils.ts @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { getBytes, hexlify, zeroPadValue } from "ethers"; import fs from "fs"; +import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; import { fileURLToPath } from "node:url"; import path from "path"; @@ -23,6 +24,34 @@ export function committeeHashFromLimbs(hi: string, lo: string): string { export const deploymentsFile = path.join("deployed_contracts.json"); +/** Hardhat network names used for local development. */ +export const LOCAL_DEPLOYMENT_NETWORKS = [ + "localhost", + "hardhat", + "anvil", + "ganache", +] as const; + +/** + * Legacy deployment bucket keys written when scripts used `provider.getNetwork().name` + * (ethers reports "default" / "undefined" on local chains). Cleared with local deploys. + */ +export const LEGACY_LOCAL_DEPLOYMENT_ALIASES = [ + "default", + "undefined", +] as const; + +/** + * Chain key for `deployed_contracts.json`. Use Hardhat's network name, not the provider's + * `network.name` (which is often `"default"` on localhost and does not match clean/deploy). + */ +export const getDeploymentChain = (hre: HardhatRuntimeEnvironment): string => + hre.globalOptions.network ?? "localhost"; + +export const isLocalDeploymentChain = (chain: string): boolean => + (LOCAL_DEPLOYMENT_NETWORKS as readonly string[]).includes(chain) || + (LEGACY_LOCAL_DEPLOYMENT_ALIASES as readonly string[]).includes(chain); + /** Monorepo root (`enclave/`), resolved from `packages/enclave-contracts/scripts/`. */ export const REPO_ROOT = path.resolve( path.dirname(fileURLToPath(import.meta.url)), @@ -309,6 +338,41 @@ export const cleanDeployments = (network: string): void => { fs.writeFileSync(deploymentsFile, JSON.stringify(deployments, null, 2)); }; +/** + * Remove deployment records for a local Hardhat network and legacy provider-name buckets. + */ +export const cleanLocalDeployments = (network: string): void => { + const isLocalNetwork = + (LOCAL_DEPLOYMENT_NETWORKS as readonly string[]).includes(network) || + (LEGACY_LOCAL_DEPLOYMENT_ALIASES as readonly string[]).includes(network); + + const targets = new Set([network]); + if (isLocalNetwork) { + for (const name of LOCAL_DEPLOYMENT_NETWORKS) { + targets.add(name); + } + for (const alias of LEGACY_LOCAL_DEPLOYMENT_ALIASES) { + targets.add(alias); + } + } + + if (!fs.existsSync(deploymentsFile)) { + return; + } + + const deployments = readAllDeployments(); + let changed = false; + for (const key of targets) { + if (deployments[key]) { + delete deployments[key]; + changed = true; + } + } + if (changed) { + fs.writeFileSync(deploymentsFile, JSON.stringify(deployments, null, 2)); + } +}; + /** * Check if two arrays are equal by checking the values inside * @param arr1 - The first array diff --git a/packages/enclave-contracts/tasks/ciphernode.ts b/packages/enclave-contracts/tasks/ciphernode.ts index afd8f2096..736ac5e26 100644 --- a/packages/enclave-contracts/tasks/ciphernode.ts +++ b/packages/enclave-contracts/tasks/ciphernode.ts @@ -312,6 +312,13 @@ export const ciphernodeAdminAdd = task( ) => { const connection = await hre.network.connect(); const { ethers } = connection; + const provider = ethers.provider; + + if (!provider) { + throw new Error( + "No provider on Hardhat network connection. Use --network localhost with Anvil running.", + ); + } if (ciphernodeAddress === ZeroAddress) { throw new Error( @@ -319,14 +326,10 @@ export const ciphernodeAdminAdd = task( ); } - let adminWallet; - if (adminPrivateKey) { - adminWallet = new ethers.Wallet(adminPrivateKey, ethers.provider); - } else { - const anvilFirstKey = - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; - adminWallet = new ethers.Wallet(anvilFirstKey, ethers.provider); - } + const [defaultSigner] = await ethers.getSigners(); + const adminWallet = adminPrivateKey + ? new ethers.Wallet(adminPrivateKey, provider) + : defaultSigner; console.log(`Admin wallet: ${adminWallet.address}`); console.log(`Registering ciphernode: ${ciphernodeAddress}`); @@ -334,24 +337,38 @@ export const ciphernodeAdminAdd = task( const { deployAndSaveBondingRegistry } = await import( "../scripts/deployAndSave/bondingRegistry" ); - const { bondingRegistry } = await deployAndSaveBondingRegistry({ hre }); - + const { deployAndSaveEnclaveTicketToken } = await import( + "../scripts/deployAndSave/enclaveTicketToken" + ); const { deployAndSaveEnclaveToken } = await import( "../scripts/deployAndSave/enclaveToken" ); - const { enclaveToken } = await deployAndSaveEnclaveToken({ hre }); - const { deployAndSaveMockStableToken } = await import( "../scripts/deployAndSave/mockStableToken" ); + const { bondingRegistry } = await deployAndSaveBondingRegistry({ hre }); + const { enclaveToken } = await deployAndSaveEnclaveToken({ hre }); + const { enclaveTicketToken } = await deployAndSaveEnclaveTicketToken({ + hre, + }); const { mockStableToken: mockUSDC } = await deployAndSaveMockStableToken({ hre, + initialSupply: 1_000_000, }); + const bondingRegistryAddress = await bondingRegistry.getAddress(); + const registryCode = await provider.getCode(bondingRegistryAddress); + if (registryCode === "0x") { + throw new Error( + `BondingRegistry not deployed at ${bondingRegistryAddress}. ` + + "Run pnpm evm:deploy with Anvil on localhost:8545 first.", + ); + } + const enclaveTokenConnected = enclaveToken.connect(adminWallet); const mockUSDCConnected = mockUSDC.connect(adminWallet); - const ticketTokenAddress = await bondingRegistry.ticketToken(); + const ticketTokenAddress = await enclaveTicketToken.getAddress(); try { const licenseBondWei = ethers.parseEther(licenseBondAmount); @@ -384,15 +401,11 @@ export const ciphernodeAdminAdd = task( console.log( "Step 3: Impersonating ciphernode for license operations...", ); - await connection.provider.request({ - method: "hardhat_impersonateAccount", - params: [ciphernodeAddress], - }); - - await connection.provider.request({ - method: "hardhat_setBalance", - params: [ciphernodeAddress, "0x1000000000000000000000"], - }); + await provider.send("anvil_impersonateAccount", [ciphernodeAddress]); + await provider.send("anvil_setBalance", [ + ciphernodeAddress, + "0x1000000000000000000000", + ]); const ciphernodeSigner = await ethers.getSigner(ciphernodeAddress); const enclaveTokenAsCiphernode = enclaveToken.connect(ciphernodeSigner); @@ -416,10 +429,9 @@ export const ciphernodeAdminAdd = task( "Operator registered (automatically added to CiphernodeRegistry)", ); - await connection.provider.request({ - method: "hardhat_stopImpersonatingAccount", - params: [ciphernodeAddress], - }); + await provider.send("anvil_stopImpersonatingAccount", [ + ciphernodeAddress, + ]); console.log("Step 4: Adding ticket balance via admin..."); @@ -429,15 +441,11 @@ export const ciphernodeAdminAdd = task( ); await approveUsdcTx.wait(); - await connection.provider.request({ - method: "hardhat_impersonateAccount", - params: [ciphernodeAddress], - }); - - await connection.provider.request({ - method: "hardhat_setBalance", - params: [ciphernodeAddress, "0x1000000000000000000000"], - }); + await provider.send("anvil_impersonateAccount", [ciphernodeAddress]); + await provider.send("anvil_setBalance", [ + ciphernodeAddress, + "0x1000000000000000000000", + ]); const ciphernodeSigner2 = await ethers.getSigner(ciphernodeAddress); const bondingRegistryAsCiphernode2 = @@ -461,10 +469,9 @@ export const ciphernodeAdminAdd = task( await addTicketTx.wait(); console.log(`Ticket balance added: ${ticketAmount} USDC worth`); - await connection.provider.request({ - method: "hardhat_stopImpersonatingAccount", - params: [ciphernodeAddress], - }); + await provider.send("anvil_stopImpersonatingAccount", [ + ciphernodeAddress, + ]); const isRegistered = await bondingRegistry.isRegistered(ciphernodeAddress); diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index bb69284e9..ad2716520 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -385,6 +385,7 @@ export const publishCommittee = task( publicKey, pkCommitment, proof, + "0x", ); console.log("Publishing committee... ", tx.hash); diff --git a/packages/enclave-contracts/tasks/program.ts b/packages/enclave-contracts/tasks/program.ts index b9104227c..056355e76 100644 --- a/packages/enclave-contracts/tasks/program.ts +++ b/packages/enclave-contracts/tasks/program.ts @@ -7,6 +7,8 @@ import fs from "fs"; import { task } from "hardhat/config"; import { ArgumentType } from "hardhat/types/arguments"; +import { readDeploymentArgs } from "../scripts/utils"; + export const publishInput = task( "e3-program:publishInput", "Publish input for an E3 program", @@ -29,11 +31,13 @@ export const publishInput = task( defaultValue: "", type: ArgumentType.STRING, }) - // MockProgram + // MockProgram. Defaults to the address in deployed_contracts.json for the + // active network; pass --program-address to override. .addOption({ name: "programAddress", - description: "Address of the E3 program", - defaultValue: "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", + description: + "Address of the E3 program (defaults to deployed MockE3Program)", + defaultValue: "", type: ArgumentType.STRING, }) .setAction(async () => ({ @@ -47,10 +51,18 @@ export const publishInput = task( const [signer] = await ethers.getSigners(); let actualProgramAddress = programAddress; - if (programAddress === "") { - actualProgramAddress = await deployAndSaveMockProgram({ hre }).then( - ({ e3Program }) => e3Program.getAddress(), + if (!actualProgramAddress) { + const deployed = readDeploymentArgs( + "MockE3Program", + hre.globalOptions.network, ); + if (deployed?.address) { + actualProgramAddress = deployed.address; + } else { + actualProgramAddress = await deployAndSaveMockProgram({ hre }).then( + ({ e3Program }) => e3Program.getAddress(), + ); + } } const program = MockE3Program__factory.connect( @@ -62,12 +74,59 @@ export const publishInput = task( if (dataFile) { const file = fs.readFileSync(dataFile); - dataToSend = file.toString(); + // Hex-encode binary file contents so ethers ABI-encodes them as `bytes`. + dataToSend = "0x" + file.toString("hex"); } await program.publishInput(e3Id, dataToSend); - console.log(`Input published`); + console.log(`Input published to ${actualProgramAddress} (e3Id=${e3Id})`); + }, + })) + .build(); + +// Wire MockE3Program → Enclave so `publishInput` forwards to +// `publishCiphertextOutput`. Off by default; the proof-aggregation integration +// flow opts in by calling this once after deploy. The non-aggregation `base` +// flow does NOT wire it, preserving the pre-existing fake_encrypt path which +// posts the ciphertext via `e3:publishCiphertext` directly. +export const setMockProgramEnclave = task( + "e3-program:setMockEnclave", + "Wire MockE3Program → Enclave for the proof-aggregation integration test", +) + .setAction(async () => ({ + default: async (_args, hre) => { + const { ethers } = await hre.network.connect(); + const [signer] = await ethers.getSigners(); + const network = hre.globalOptions.network; + + const mockArgs = readDeploymentArgs("MockE3Program", network); + const enclaveArgs = readDeploymentArgs("Enclave", network); + if (!mockArgs?.address || !enclaveArgs?.address) { + throw new Error( + "MockE3Program or Enclave deployment not found; deploy first.", + ); + } + + // Use ABI fragments directly so this works even when typechain types + // haven't been regenerated. + const mockProgram = new ethers.Contract( + mockArgs.address, + [ + "function enclave() view returns (address)", + "function setEnclave(address) external", + ], + signer, + ); + const current: string = await mockProgram.enclave(); + if (current.toLowerCase() === enclaveArgs.address.toLowerCase()) { + console.log(`MockE3Program already wired to ${enclaveArgs.address}`); + return; + } + await mockProgram.setEnclave(enclaveArgs.address); + console.log( + `MockE3Program ${mockArgs.address} → Enclave ${enclaveArgs.address}`, + ); }, })) .build(); diff --git a/packages/enclave-contracts/tasks/utils.ts b/packages/enclave-contracts/tasks/utils.ts index 040cda2cf..ace9e09ea 100644 --- a/packages/enclave-contracts/tasks/utils.ts +++ b/packages/enclave-contracts/tasks/utils.ts @@ -6,7 +6,7 @@ import { task } from "hardhat/config"; import { ArgumentType } from "hardhat/types/arguments"; -import { cleanDeployments } from "../scripts/utils"; +import { cleanLocalDeployments } from "../scripts/utils"; export const cleanDeploymentsTask = task( "utils:clean-deployments", @@ -20,7 +20,7 @@ export const cleanDeploymentsTask = task( }) .setAction(async () => ({ default: ({ chain }) => { - cleanDeployments(chain); + cleanLocalDeployments(chain); }, })) .build(); diff --git a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts index c8e8006ee..82435c829 100644 --- a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts @@ -103,7 +103,7 @@ describe("BfvDecryptionVerifier", function () { return { bfvDecryptionVerifier: dv, mockCircuit: mc }; }; - describe("reverts / false", function () { + describe("reverts", function () { it("reverts on invalid proof encoding", async function () { const { bfvDecryptionVerifier } = await loadFixture( deployWithMockCircuit, @@ -120,7 +120,7 @@ describe("BfvDecryptionVerifier", function () { ).to.be.revert(ethers); }); - it("returns false when publicInputs.length is below expected", async function () { + it("reverts with BadPublicInputsLen when publicInputs.length is below expected", async function () { const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); @@ -134,15 +134,19 @@ describe("BfvDecryptionVerifier", function () { const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); - const result = await bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - ethers.ZeroHash, - proof, + await expect( + bfvDecryptionVerifier.verify.staticCall( + plaintextHash, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError( + bfvDecryptionVerifier, + "BadPublicInputsLen", ); - expect(result).to.equal(false); }); - it("returns false when publicInputs.length exceeds expected", async function () { + it("reverts with BadPublicInputsLen when publicInputs.length exceeds expected", async function () { const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); @@ -156,15 +160,19 @@ describe("BfvDecryptionVerifier", function () { const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); - const result = await bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - ethers.ZeroHash, - proof, + await expect( + bfvDecryptionVerifier.verify.staticCall( + plaintextHash, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError( + bfvDecryptionVerifier, + "BadPublicInputsLen", ); - expect(result).to.equal(false); }); - it("returns false when c6_fold key hash does not match", async function () { + it("reverts with BadC6FoldKeyHash when c6_fold key hash does not match", async function () { const revertingVerifier = await ( await ethers.getContractFactory("RevertOnVerifyCircuitVerifier") ).deploy(); @@ -189,15 +197,19 @@ describe("BfvDecryptionVerifier", function () { const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); - const result = await bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - ethers.ZeroHash, - proof, + await expect( + bfvDecryptionVerifier.verify.staticCall( + plaintextHash, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError( + bfvDecryptionVerifier, + "BadC6FoldKeyHash", ); - expect(result).to.equal(false); }); - it("returns false when c7 key hash does not match", async function () { + it("reverts with BadC7KeyHash when c7 key hash does not match", async function () { const revertingVerifier = await ( await ethers.getContractFactory("RevertOnVerifyCircuitVerifier") ).deploy(); @@ -222,15 +234,16 @@ describe("BfvDecryptionVerifier", function () { const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x01", publicInputs); - const result = await bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + await expect( + bfvDecryptionVerifier.verify.staticCall( + plaintextHash, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvDecryptionVerifier, "BadC7KeyHash"); }); - it("returns false when plaintext hash mismatch", async function () { + it("reverts with BadPlaintextHash when plaintext hash mismatch", async function () { const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); @@ -241,15 +254,21 @@ describe("BfvDecryptionVerifier", function () { const wrongHash = ethers.keccak256("0x0000"); const proof = encodeProof("0x01", publicInputs); - const result = await bfvDecryptionVerifier.verify.staticCall( - wrongHash, - ethers.ZeroHash, - proof, + await expect( + bfvDecryptionVerifier.verify.staticCall( + wrongHash, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError( + bfvDecryptionVerifier, + "BadPlaintextHash", ); - expect(result).to.equal(false); }); it("returns false when circuit verifier returns false", async function () { + // Circuit-verify failure is still expressed as `return false` because the + // bool return reflects the underlying Honk verifier's bool result. const { bfvDecryptionVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); @@ -268,7 +287,7 @@ describe("BfvDecryptionVerifier", function () { expect(result).to.equal(false); }); - it("returns false when constructor expected hashes do not match proof", async function () { + it("reverts with BadC6FoldKeyHash when constructor expected hashes do not match proof", async function () { const { mockCircuit } = await loadFixture(deployWithMockCircuit); await mockCircuit.setReturnValue(true); const mockAddr = await mockCircuit.getAddress(); @@ -288,12 +307,18 @@ describe("BfvDecryptionVerifier", function () { const plaintextHash = plaintextToHash(messageCoeffs); const proof = encodeProof("0x0102", publicInputs); - const result = await bfvDecryptionVerifier.verify.staticCall( - plaintextHash, - ethers.ZeroHash, - proof, + // The proof's c6_fold hash is checked first and won't match the + // constructor's "wrong-c6", so BadC6FoldKeyHash fires. + await expect( + bfvDecryptionVerifier.verify.staticCall( + plaintextHash, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError( + bfvDecryptionVerifier, + "BadC6FoldKeyHash", ); - expect(result).to.equal(false); }); }); diff --git a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts index 88f81fb1a..b615d9441 100644 --- a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts @@ -71,7 +71,7 @@ describe("BfvPkVerifier", function () { return { bfvPkVerifier: pk, mockCircuit: mc }; }; - describe("reverts / false", function () { + describe("reverts", function () { it("reverts on invalid proof encoding", async function () { const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); const pkCommitment = ethers.keccak256("0x1234"); @@ -86,33 +86,35 @@ describe("BfvPkVerifier", function () { ).to.be.revert(ethers); }); - it("returns false when publicInputs is empty", async function () { + it("reverts with BadPublicInputsLen when publicInputs is empty", async function () { const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); const pkCommitment = ethers.keccak256("0x1234"); const proof = encodeProof("0x01", []); - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + await expect( + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); - it("returns false when publicInputs has only one entry", async function () { + it("reverts with BadPublicInputsLen when publicInputs has only one entry", async function () { const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); const pkCommitment = ethers.keccak256("0xabcd"); const proof = encodeProof("0x01", [pkCommitment]); - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + await expect( + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); - it("returns false when publicInputs has trailing elements past expected length", async function () { + it("reverts with BadPublicInputsLen when publicInputs has trailing elements past expected length", async function () { const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); const pkCommitment = ethers.keccak256("0xabcd"); const proof = encodeProof("0x01", [ @@ -120,15 +122,16 @@ describe("BfvPkVerifier", function () { ethers.ZeroHash, ]); - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + await expect( + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); - it("returns false when publicInputs has only pub params (length 7, no return fields)", async function () { + it("reverts with BadPublicInputsLen when publicInputs has only pub params (no return fields)", async function () { const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); const pkCommitment = ethers.keccak256("0xabcd"); const [hi, lo] = committeeHashLimbs(ethers.ZeroHash); @@ -141,31 +144,16 @@ describe("BfvPkVerifier", function () { pkCommitment, ]); - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); - }); - - it("returns false when publicInputs has only vk hashes (no pkCommitment slot)", async function () { - const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); - const pkCommitment = ethers.keccak256("0xabcd"); - const proof = encodeProof("0x01", [ - EXPECTED_NODES_FOLD_KEY_HASH, - EXPECTED_C5_KEY_HASH, - ]); - - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + await expect( + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); - it("returns false when publicInputs has only vk hashes (no pkCommitment slot)", async function () { + it("reverts with BadPublicInputsLen when publicInputs has only vk hashes (no pkCommitment slot)", async function () { const { bfvPkVerifier } = await loadFixture(deployWithMockCircuit); const pkCommitment = ethers.keccak256("0xabcd"); const proof = encodeProof("0x01", [ @@ -173,15 +161,16 @@ describe("BfvPkVerifier", function () { EXPECTED_C5_KEY_HASH, ]); - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + await expect( + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); - it("returns false when nodes_fold key hash does not match", async function () { + it("reverts with BadNodesFoldKeyHash when nodes_fold key hash does not match", async function () { const revertingVerifier = await ( await ethers.getContractFactory("RevertOnVerifyCircuitVerifier") ).deploy(); @@ -205,15 +194,16 @@ describe("BfvPkVerifier", function () { ), ); - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + await expect( + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "BadNodesFoldKeyHash"); }); - it("returns false when c5 key hash does not match", async function () { + it("reverts with BadC5KeyHash when c5 key hash does not match", async function () { const revertingVerifier = await ( await ethers.getContractFactory("RevertOnVerifyCircuitVerifier") ).deploy(); @@ -237,15 +227,16 @@ describe("BfvPkVerifier", function () { ), ); - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + await expect( + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "BadC5KeyHash"); }); - it("returns false when pkCommitment does not match last public input", async function () { + it("reverts with BadPkCommitment when pkCommitment does not match last public input", async function () { const { bfvPkVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); @@ -255,15 +246,18 @@ describe("BfvPkVerifier", function () { const wrong = ethers.keccak256("0x1234"); const proof = encodeProof("0x01", minimalDkgPublicInputs(wrong)); - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + await expect( + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPkCommitment"); }); it("returns false when circuit verifier returns false", async function () { + // Circuit-verify failure is still expressed as `return false` because the + // bool return reflects the underlying Honk verifier's bool result. const { bfvPkVerifier, mockCircuit } = await loadFixture( deployWithMockCircuit, ); @@ -280,7 +274,7 @@ describe("BfvPkVerifier", function () { expect(result).to.equal(false); }); - it("returns false when constructor expected hashes do not match proof", async function () { + it("reverts with BadNodesFoldKeyHash when constructor expected hashes do not match proof", async function () { const { mockCircuit } = await loadFixture(deployWithMockCircuit); await mockCircuit.setReturnValue(true); const mockAddr = await mockCircuit.getAddress(); @@ -298,12 +292,15 @@ describe("BfvPkVerifier", function () { const pkCommitment = ethers.keccak256("0xabcd"); const proof = encodeProof("0x0102", minimalDkgPublicInputs(pkCommitment)); - const result = await bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ); - expect(result).to.equal(false); + // The proof's nodes_fold hash is checked first and won't match the + // constructor's "wrong-nodes-fold", so BadNodesFoldKeyHash fires. + await expect( + bfvPkVerifier.verify.staticCall( + pkCommitment, + ethers.ZeroHash, + proof, + ), + ).to.be.revertedWithCustomError(bfvPkVerifier, "BadNodesFoldKeyHash"); }); }); diff --git a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts index 8b39e0345..b86a67c11 100644 --- a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -441,7 +441,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); // Verify stage transitioned to KeyPublished (after publishCommittee which calls onKeyPublished) stage = await enclave.getE3Stage(0); @@ -481,7 +481,9 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await expect(registry.publishCommittee(0, publicKey, pkCommitment, "0x")) + await expect( + registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"), + ) .to.emit(enclave, "CommitteeFormed") .withArgs(0); }); @@ -739,7 +741,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); // 2. Wait past compute deadline → mark as failed const e3 = await enclave.getE3(0); @@ -838,7 +840,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); // 2. Fail via compute timeout const e3 = await enclave.getE3(0); @@ -1085,7 +1087,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); stage = await enclave.getE3Stage(0); expect(stage).to.equal(3); // KeyPublished @@ -1159,7 +1161,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); stage = await enclave.getE3Stage(0); expect(stage).to.equal(3); // KeyPublished @@ -1397,7 +1399,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); expect(await enclave.getE3Stage(0)).to.equal(3); // KeyPublished @@ -1523,7 +1525,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); expect(await enclave.getE3Stage(0)).to.equal(3); // KeyPublished @@ -1577,7 +1579,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { const publicKey = "0x1234567890abcdef1234567890abcdef"; const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(0, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(0, publicKey, pkCommitment, "0x", "0x"); // Publish outputs const e3 = await enclave.getE3(0); diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 01790ce68..5524ba2a6 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -69,6 +69,10 @@ describe("Enclave", function () { const data = "0xda7a"; const proof = "0x1337"; + const DKG_FOLD_ATTESTATION_TYPEHASH = ethers.id( + "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", + ); + /** ABI-encoded fake DKG proof for `MockPkVerifier` (last public input must equal `pkCommitment`). */ const encodeMockDkgProof = (pkCommitment: string): string => ethers.AbiCoder.defaultAbiCoder().encode( @@ -76,12 +80,122 @@ describe("Enclave", function () { ["0x", [pkCommitment]], ); + /** Public inputs layout for `DkgFoldAttestationVerifier` with `h` honest parties. */ + const encodeMockDkgProofForAttestation = ( + pkCommitment: string, + partyIds: number[], + skCommits: string[], + esmCommits: string[], + ): string => { + const h = partyIds.length; + const publicInputs: string[] = Array.from( + { length: 6 + 3 * h }, + () => ethers.ZeroHash, + ); + publicInputs[publicInputs.length - 1] = pkCommitment; + for (let i = 0; i < h; i++) { + publicInputs[2 + i] = ethers.zeroPadValue( + ethers.toBeHex(partyIds[i]), + 32, + ); + publicInputs[5 + h + i] = skCommits[i]; + publicInputs[5 + 2 * h + i] = esmCommits[i]; + } + return ethers.AbiCoder.defaultAbiCoder().encode( + ["bytes", "bytes32[]"], + ["0x", publicInputs], + ); + }; + + const signFoldAttestation = async ( + signer: Signer, + chainId: bigint, + e3Id: number, + partyId: number, + skAggCommit: string, + esmAggCommit: string, + ): Promise => { + const digest = ethers.keccak256( + ethers.AbiCoder.defaultAbiCoder().encode( + ["bytes32", "uint256", "uint256", "uint256", "bytes32", "bytes32"], + [ + DKG_FOLD_ATTESTATION_TYPEHASH, + chainId, + e3Id, + partyId, + skAggCommit, + esmAggCommit, + ], + ), + ); + return signer.signMessage(ethers.getBytes(digest)); + }; + + /** Proof + attestation bundle for `publishCommittee` when proof aggregation is enabled. */ + const buildMockAggregationPublishArgs = async ( + operators: Signer[], + e3Id: number, + publicKey: string, + ): Promise<{ proof: string; bundle: string }> => { + const pkCommitment = ethers.keccak256(publicKey); + const h = operators.length; + const partyIds = Array.from({ length: h }, (_, i) => i); + const skCommits = partyIds.map((i) => ethers.id(`sk-${e3Id}-${i}`)); + const esmCommits = partyIds.map((i) => ethers.id(`esm-${e3Id}-${i}`)); + const proof = encodeMockDkgProofForAttestation( + pkCommitment, + partyIds, + skCommits, + esmCommits, + ); + + const { chainId } = await ethers.provider.getNetwork(); + const attestations: { + partyId: number; + skAggCommit: string; + esmAggCommit: string; + signature: string; + }[] = []; + const bindings: { partyId: number; node: string }[] = []; + + for (let i = 0; i < h; i++) { + const operator = operators[i]!; + const node = await operator.getAddress(); + const partyId = partyIds[i]!; + attestations.push({ + partyId, + skAggCommit: skCommits[i]!, + esmAggCommit: esmCommits[i]!, + signature: await signFoldAttestation( + operator, + chainId, + e3Id, + partyId, + skCommits[i]!, + esmCommits[i]!, + ), + }); + bindings.push({ partyId, node }); + } + + const bundle = ethers.AbiCoder.defaultAbiCoder().encode( + [ + "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", + "tuple(uint256 partyId, address node)[]", + ], + [attestations, bindings], + ); + + return { proof, bundle }; + }; + const setupAndPublishCommittee = async ( registry: any, e3Id: number, publicKey: string, operators: Signer[], committeeProof: string = "0x", + attestationBundle: string = "0x", ): Promise => { for (const operator of operators) { await registry.connect(operator).submitTicket(e3Id, 1); @@ -94,6 +208,7 @@ describe("Enclave", function () { publicKey, pkCommitment, committeeProof, + attestationBundle, ); }; @@ -264,6 +379,13 @@ describe("Enclave", function () { await mockPkVerifier.getAddress(), ); + const dkgFoldAttestationVerifier = await ethers.deployContract( + "DkgFoldAttestationVerifier", + ); + await ciphernodeRegistryContract.setDkgFoldAttestationVerifier( + await dkgFoldAttestationVerifier.getAddress(), + ); + // ── Operators ───────────────────────────────────────────────────────────── await licenseToken.setTransferRestriction(false); @@ -1062,13 +1184,19 @@ describe("Enclave", function () { proofAggregationEnabled: true, }); - const pkCommitment = ethers.keccak256(data); + const operators = [operator1, operator2, operator3]; + const { proof, bundle } = await buildMockAggregationPublishArgs( + operators, + e3Id, + data, + ); await setupAndPublishCommittee( ciphernodeRegistryContract, e3Id, data, - [operator1, operator2, operator3], - encodeMockDkgProof(pkCommitment), + operators, + proof, + bundle, ); await mine(2, { interval: inputWindowDuration }); await enclave.publishCiphertextOutput(e3Id, data, proof); diff --git a/packages/enclave-contracts/test/Pricing/Pricing.spec.ts b/packages/enclave-contracts/test/Pricing/Pricing.spec.ts index 58eb88821..4d6fb00a1 100644 --- a/packages/enclave-contracts/test/Pricing/Pricing.spec.ts +++ b/packages/enclave-contracts/test/Pricing/Pricing.spec.ts @@ -141,7 +141,7 @@ describe("E3 Pricing", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x", "0x"); }; const setup = async () => { diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index 7ff760319..7c70fe02a 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -184,6 +184,13 @@ describe("CiphernodeRegistryOwnable", function () { await registry.setBondingRegistry(await bondingRegistry.getAddress()); await registry.setEnclave(enclaveAddress); + const dkgFoldAttestationVerifier = await ethers.deployContract( + "DkgFoldAttestationVerifier", + ); + await registry.setDkgFoldAttestationVerifier( + await dkgFoldAttestationVerifier.getAddress(), + ); + // ── Mock E3 Program & Decryption Verifier ────────────────────────────────── const { mockE3Program } = await ignition.deploy(MockE3ProgramModule); const { mockDecryptionVerifier } = await ignition.deploy( @@ -423,21 +430,23 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); + // `topNodes` is sorted by ascending address on-chain, so derive the + // expected order rather than hardcoding submission order. + const sortedCommittee = [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ].sort((a, b) => + BigInt(a) < BigInt(b) ? -1 : BigInt(a) > BigInt(b) ? 1 : 0, + ); + await expect( - registry.connect(notTheOwner).publishCommittee(0, data, dataHash, "0x"), + registry + .connect(notTheOwner) + .publishCommittee(0, data, dataHash, "0x", "0x"), ) .to.emit(registry, "CommitteePublished") - .withArgs( - 0, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - dataHash, - "0x", - ); + .withArgs(0, sortedCommittee, data, dataHash, "0x"); }); it("stores the public key of the committee", async function () { const { @@ -462,7 +471,7 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); - await registry.publishCommittee(0, data, dataHash, "0x"); + await registry.publishCommittee(0, data, dataHash, "0x", "0x"); expect(await registry.committeePublicKey(0)).to.equal(dataHash); }); it("emits a CommitteePublished event", async function () { @@ -489,19 +498,20 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); - await expect(await registry.publishCommittee(0, data, dataHash, "0x")) + // `topNodes` is sorted by ascending address on-chain. + const sortedCommittee = [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ].sort((a, b) => + BigInt(a) < BigInt(b) ? -1 : BigInt(a) > BigInt(b) ? 1 : 0, + ); + + await expect( + await registry.publishCommittee(0, data, dataHash, "0x", "0x"), + ) .to.emit(registry, "CommitteePublished") - .withArgs( - 0, - [ - await operator1.getAddress(), - await operator2.getAddress(), - await operator3.getAddress(), - ], - data, - dataHash, - "0x", - ); + .withArgs(0, sortedCommittee, data, dataHash, "0x"); }); }); @@ -660,7 +670,7 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(e3Id, 1); await finalizeCommitteeAfterWindow(registry, e3Id); - await registry.publishCommittee(e3Id, data, dataHash, "0x"); + await registry.publishCommittee(e3Id, data, dataHash, "0x", "0x"); expect(await registry.committeePublicKey(e3Id)).to.equal(dataHash); }); it("reverts if the committee has not been published", async function () { diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index 032c50a07..177cad5bd 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -344,7 +344,13 @@ describe("Committee Expulsion & Fault Tolerance", function () { const publicKey = ethers.toUtf8Bytes("fake-public-key"); const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x"); + await registry.publishCommittee( + e3Id, + publicKey, + pkCommitment, + "0x", + "0x", + ); } // ── Return ───────────────────────────────────────────────────────────────── diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 0b0e32174..ee6c4d11d 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -4,22 +4,22 @@ chains: contracts: enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 27 + deploy_block: 17 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 23 + deploy_block: 13 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 24 + deploy_block: 14 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 11 + deploy_block: 12 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 18 + deploy_block: 8 e3_program: - address: "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570" - deploy_block: 46 + address: "0x851356ae760d987E095750cCeb3bC6014560891C" + deploy_block: 24 program: dev: true nodes: diff --git a/tests/integration/base.sh b/tests/integration/base.sh index 89450ca2c..59b7fe685 100755 --- a/tests/integration/base.sh +++ b/tests/integration/base.sh @@ -18,7 +18,17 @@ until curl -sf -X POST http://localhost:8545 -H 'Content-Type: application/json' done pnpm evm:clean -pnpm evm:deploy --network localhost + +if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then + heading "Deploy contracts (ZK verification + fold attestation verifier)" + ENABLE_ZK_VERIFICATION=true pnpm evm:deploy +else + heading "Deploy contracts (mock verifiers)" + pnpm evm:deploy +fi + +heading "Sync tests/integration/enclave.config.yaml from deployed_contracts.json" +(cd "$ROOT_DIR/packages/enclave-contracts" && pnpm utils:sync-integration-config) enclave_wallet_set cn1 "$PRIVATE_KEY_CN1" enclave_wallet_set cn2 "$PRIVATE_KEY_CN2" @@ -26,7 +36,7 @@ enclave_wallet_set cn3 "$PRIVATE_KEY_CN3" enclave_wallet_set cn4 "$PRIVATE_KEY_CN4" enclave_wallet_set cn5 "$PRIVATE_KEY_CN5" -heading "Setup ZK prover" +heading "Setup ZK prover (bb binary; circuits staged in prebuild when proof aggregation is on)" $ENCLAVE_BIN noir setup # start swarm @@ -53,7 +63,7 @@ pnpm ciphernode:add --ciphernode-address $CIPHERNODE_ADDRESS_4 --network localho heading "Add ciphernode $CIPHERNODE_ADDRESS_5" pnpm ciphernode:add --ciphernode-address $CIPHERNODE_ADDRESS_5 --network localhost -heading "Request Committee" +heading "Request Committee (proof-aggregation-enabled=$PROOF_AGGREGATION_ENABLED)" ENCODED_PARAMS=0x$($SCRIPT_DIR/lib/pack_e3_params.sh \ --moduli 0xffffee001 \ @@ -73,29 +83,50 @@ pnpm committee:new \ --input-window-end "$INPUT_WINDOW_END" \ --e3-params "$ENCODED_PARAMS" \ --committee-size 0 \ - --proof-aggregation-enabled false + --proof-aggregation-enabled "$PROOF_AGGREGATION_ENABLED" -wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" +wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" "$INTEGRATION_DKG_TIMEOUT" + +if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then + heading "Verify active aggregator (proof aggregation / DKG path)" + ACTIVE_AGG_ADDRESS=$(wait_for_active_aggregator_address 0 "$INTEGRATION_DKG_TIMEOUT") + echo "Active aggregator: $ACTIVE_AGG_ADDRESS" +fi heading "Query events via daemon REST API" daemon_query_events cn1 "$SCRIPT_DIR/output/events.txt" check_last_line "$SCRIPT_DIR/output/events.txt" '{"Next":10}' -heading "Mock encrypted plaintext" -$SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" +if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then + heading "Wire MockE3Program → Enclave so publishInput triggers decryption" + pnpm e3-program:setMockEnclave --network localhost -heading "Mock publish input e3-id" -pnpm e3-program:publishInput --network localhost --e3-id 0 --data 0x12345678 + heading "Encrypt plaintext under the published committee pubkey" + $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" + waiton "$SCRIPT_DIR/output/output.bin" -sleep 4 + heading "Publish E3 input (forwards to publishCiphertextOutput; nodes run decryption with ZK proofs)" + pnpm e3-program:publishInput --network localhost --e3-id 0 --data-file "$SCRIPT_DIR/output/output.bin" -waiton "$SCRIPT_DIR/output/output.bin" + heading "Wait for on-chain plaintext (BFV decryption verifier)" + wait_for_plaintext_output 0 "$SCRIPT_DIR/output/plaintext.txt" "$INTEGRATION_DKG_TIMEOUT" +else + heading "Mock encrypted plaintext" + $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" -heading "Publish ciphertext to EVM" -pnpm e3:publishCiphertext --e3-id 0 --network localhost --data-file "$SCRIPT_DIR/output/output.bin" --proof 0x12345678 + heading "Mock publish input e3-id" + pnpm e3-program:publishInput --network localhost --e3-id 0 --data 0x12345678 -wait_for_plaintext_output 0 "$SCRIPT_DIR/output/plaintext.txt" + sleep 4 + + waiton "$SCRIPT_DIR/output/output.bin" + + heading "Publish ciphertext to EVM" + pnpm e3:publishCiphertext --e3-id 0 --network localhost --data-file "$SCRIPT_DIR/output/output.bin" --proof 0x12345678 + + wait_for_plaintext_output 0 "$SCRIPT_DIR/output/plaintext.txt" +fi ACTUAL=$(cut -d',' -f1,2 $SCRIPT_DIR/output/plaintext.txt) diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index c4d778416..b4e5d478f 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -1,25 +1,26 @@ +# Sync from packages/enclave-contracts/deployed_contracts.json (localhost) after each `pnpm evm:deploy`. chains: - name: "localhost" rpc_url: "ws://localhost:8545" contracts: e3_program: - address: "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570" - deploy_block: 1 # Set to actual deploy block + address: "0x851356ae760d987E095750cCeb3bC6014560891C" + deploy_block: 22 enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 1 # Set to actual deploy block + deploy_block: 15 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 1 # Set to actual deploy block + deploy_block: 11 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 1 # Set to actual deploy block + deploy_block: 12 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 1 # Set to actual deploy block + deploy_block: 10 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 1 # Set to actual deploy block + deploy_block: 6 program: dev: true diff --git a/tests/integration/fns.sh b/tests/integration/fns.sh index 76afc0029..9332d11a7 100644 --- a/tests/integration/fns.sh +++ b/tests/integration/fns.sh @@ -5,6 +5,10 @@ set -euo pipefail # Stricter error handling SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" PLAINTEXT="4,0" + +# Set by test.sh via export_integration_flags +PROOF_AGGREGATION_ENABLED="${PROOF_AGGREGATION_ENABLED:-false}" +INTEGRATION_DKG_TIMEOUT="${INTEGRATION_DKG_TIMEOUT:-1300}" ID=$(date +%s) if [[ "$SCRIPT_DIR" != "$(pwd)" ]]; then @@ -101,7 +105,7 @@ waiton-files() { wait_for_committee_pubkey() { local e3_id="$1" local out_file="$2" - local timeout="${3:-1300}" + local timeout="${3:-$INTEGRATION_DKG_TIMEOUT}" local start_time=$(date +%s) while true; do diff --git a/tests/integration/lib/prebuild.sh b/tests/integration/lib/prebuild.sh index 84aefd6e7..8a99d21ed 100755 --- a/tests/integration/lib/prebuild.sh +++ b/tests/integration/lib/prebuild.sh @@ -1,9 +1,49 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash set -eu # Exit immediately if a command exits with a non-zero status + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" +INTEGRATION_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +INTEGRATION_NOIR="${INTEGRATION_DIR}/.enclave/noir" +VERSIONS_JSON="${ROOT_DIR}/crates/zk-prover/versions.json" + echo "" echo "PREBUILDING BINARIES..." echo "" -cd ../../crates && cargo build --bin fake_encrypt --bin pack_e3_params; +(cd "$ROOT_DIR/crates" && cargo build --bin fake_encrypt --bin pack_e3_params) echo "" echo "FINISHED PREBUILDING BINARIES" echo "" + +if [[ "${PROOF_AGGREGATION_ENABLED:-false}" == "true" ]]; then + echo "" + echo "BUILDING ZK CIRCUITS + ON-CHAIN VERIFIERS (proof aggregation enabled)..." + echo "" + + # Nodes must prove with the same VKs as deployed Honk verifiers. `enclave noir setup` + # otherwise installs the release tarball (crates/zk-prover/versions.json), which can + # disagree with locally rebuilt circuits/verifiers after `build:circuits`. + rm -rf "${INTEGRATION_NOIR}/circuits" + mkdir -p "${INTEGRATION_NOIR}/circuits" + + (cd "$ROOT_DIR" && pnpm build:circuits --preset insecure-512 -o "${INTEGRATION_NOIR}/circuits") + (cd "$ROOT_DIR" && pnpm generate:verifiers --no-compile --no-clean-targets) + + if ! command -v jq >/dev/null 2>&1; then + echo "jq is required to pin noir/version.json for integration ZK fixtures" >&2 + exit 1 + fi + REQUIRED_BB="$(jq -r '.required_bb_version' "$VERSIONS_JSON")" + REQUIRED_CIRCUITS="$(jq -r '.required_circuits_version' "$VERSIONS_JSON")" + jq -n \ + --arg bb "$REQUIRED_BB" \ + --arg circuits "$REQUIRED_CIRCUITS" \ + '{bb_version: $bb, circuits_version: $circuits}' \ + > "${INTEGRATION_NOIR}/version.json" + + echo "Staged circuits under ${INTEGRATION_NOIR}/circuits/insecure-512" + echo "Pinned noir version.json (bb=${REQUIRED_BB}, circuits=${REQUIRED_CIRCUITS})" + echo "" + echo "FINISHED BUILDING ZK CIRCUITS + VERIFIERS" + echo "" +fi diff --git a/tests/integration/persist.sh b/tests/integration/persist.sh index 8c7445dfc..7e1de7ec0 100755 --- a/tests/integration/persist.sh +++ b/tests/integration/persist.sh @@ -18,7 +18,14 @@ until curl -sf -X POST http://localhost:8545 -H 'Content-Type: application/json' done pnpm evm:clean -pnpm evm:deploy --network localhost + +if [[ "${PROOF_AGGREGATION_ENABLED:-false}" == "true" ]]; then + ENABLE_ZK_VERIFICATION=true pnpm evm:deploy +else + pnpm evm:deploy +fi + +(cd "$ROOT_DIR/packages/enclave-contracts" && pnpm utils:sync-integration-config) enclave_wallet_set cn1 "$PRIVATE_KEY_CN1" enclave_wallet_set cn2 "$PRIVATE_KEY_CN2" @@ -67,9 +74,9 @@ pnpm committee:new \ --input-window-end "$INPUT_WINDOW_END" \ --e3-params "$ENCODED_PARAMS" \ --committee-size 0 \ - --proof-aggregation-enabled false + --proof-aggregation-enabled "${PROOF_AGGREGATION_ENABLED:-false}" -wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" +wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" "${INTEGRATION_DKG_TIMEOUT:-1300}" ACTIVE_AGG_ADDRESS=$(wait_for_active_aggregator_address 0) if ! ACTIVE_AGG=$(node_name_for_address "$ACTIVE_AGG_ADDRESS"); then diff --git a/tests/integration/test.sh b/tests/integration/test.sh index 1ce544236..f8bde2cc0 100755 --- a/tests/integration/test.sh +++ b/tests/integration/test.sh @@ -4,16 +4,58 @@ set -eu # Exit immediately if a command exits with a non-zero status THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -if [[ "$*" != *"--no-prebuild"* ]]; then - "$THIS_DIR/lib/prebuild.sh" -fi +PROOF_AGGREGATION_ENABLED="${PROOF_AGGREGATION_ENABLED:-false}" +SKIP_PREBUILD=false + +parse_integration_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --proof-aggregation-enabled) + shift + PROOF_AGGREGATION_ENABLED="${1:-true}" + shift + ;; + --no-prebuild) + SKIP_PREBUILD=true + shift + ;; + *) + echo "Unknown integration argument: $1" >&2 + echo "Usage: ./test.sh [base|persist|net|restart] [--proof-aggregation-enabled true|false] [--no-prebuild]" >&2 + exit 1 + ;; + esac + done +} + +export_integration_flags() { + export PROOF_AGGREGATION_ENABLED + if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then + export ENABLE_ZK_VERIFICATION=true + export INTEGRATION_DKG_TIMEOUT="${INTEGRATION_DKG_TIMEOUT:-3600}" + else + export ENABLE_ZK_VERIFICATION=false + export INTEGRATION_DKG_TIMEOUT="${INTEGRATION_DKG_TIMEOUT:-1300}" + fi +} -if [ $# -eq 0 ]; then +if [ $# -eq 0 ]; then + export PROOF_AGGREGATION_ENABLED=false + export_integration_flags + "$THIS_DIR/lib/prebuild.sh" "$THIS_DIR/persist.sh" "$THIS_DIR/base.sh" "$THIS_DIR/net.sh" "$THIS_DIR/restart.sh" else - "$THIS_DIR/$1.sh" -fi + SCRIPT_NAME="$1" + shift + parse_integration_args "$@" + export_integration_flags + + if [[ "$SKIP_PREBUILD" != "true" ]]; then + "$THIS_DIR/lib/prebuild.sh" + fi + "$THIS_DIR/${SCRIPT_NAME}.sh" +fi From d16ef91ba8e0fb830284cdb0125d83650cb64dfc Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 17:12:40 +0200 Subject: [PATCH 17/54] format missing test --- .../test/BfvPkVerifier.spec.ts | 54 ++++--------------- 1 file changed, 9 insertions(+), 45 deletions(-) diff --git a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts index b615d9441..28f75887e 100644 --- a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts @@ -92,11 +92,7 @@ describe("BfvPkVerifier", function () { const proof = encodeProof("0x01", []); await expect( - bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ), + bfvPkVerifier.verify.staticCall(pkCommitment, ethers.ZeroHash, proof), ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); @@ -106,11 +102,7 @@ describe("BfvPkVerifier", function () { const proof = encodeProof("0x01", [pkCommitment]); await expect( - bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ), + bfvPkVerifier.verify.staticCall(pkCommitment, ethers.ZeroHash, proof), ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); @@ -123,11 +115,7 @@ describe("BfvPkVerifier", function () { ]); await expect( - bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ), + bfvPkVerifier.verify.staticCall(pkCommitment, ethers.ZeroHash, proof), ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); @@ -145,11 +133,7 @@ describe("BfvPkVerifier", function () { ]); await expect( - bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ), + bfvPkVerifier.verify.staticCall(pkCommitment, ethers.ZeroHash, proof), ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); @@ -162,11 +146,7 @@ describe("BfvPkVerifier", function () { ]); await expect( - bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ), + bfvPkVerifier.verify.staticCall(pkCommitment, ethers.ZeroHash, proof), ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPublicInputsLen"); }); @@ -195,11 +175,7 @@ describe("BfvPkVerifier", function () { ); await expect( - bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ), + bfvPkVerifier.verify.staticCall(pkCommitment, ethers.ZeroHash, proof), ).to.be.revertedWithCustomError(bfvPkVerifier, "BadNodesFoldKeyHash"); }); @@ -228,11 +204,7 @@ describe("BfvPkVerifier", function () { ); await expect( - bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ), + bfvPkVerifier.verify.staticCall(pkCommitment, ethers.ZeroHash, proof), ).to.be.revertedWithCustomError(bfvPkVerifier, "BadC5KeyHash"); }); @@ -247,11 +219,7 @@ describe("BfvPkVerifier", function () { const proof = encodeProof("0x01", minimalDkgPublicInputs(wrong)); await expect( - bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ), + bfvPkVerifier.verify.staticCall(pkCommitment, ethers.ZeroHash, proof), ).to.be.revertedWithCustomError(bfvPkVerifier, "BadPkCommitment"); }); @@ -295,11 +263,7 @@ describe("BfvPkVerifier", function () { // The proof's nodes_fold hash is checked first and won't match the // constructor's "wrong-nodes-fold", so BadNodesFoldKeyHash fires. await expect( - bfvPkVerifier.verify.staticCall( - pkCommitment, - ethers.ZeroHash, - proof, - ), + bfvPkVerifier.verify.staticCall(pkCommitment, ethers.ZeroHash, proof), ).to.be.revertedWithCustomError(bfvPkVerifier, "BadNodesFoldKeyHash"); }); }); From 68da46ca6ca6dafefdc7e56955fd55e1245a5988 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 17:18:45 +0200 Subject: [PATCH 18/54] small nits --- .../enclave-contracts/contracts/test/MockE3Program.sol | 4 ++++ .../enclave-contracts/test/BfvDecryptionVerifier.spec.ts | 6 ++++++ packages/enclave-contracts/test/BfvPkVerifier.spec.ts | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/packages/enclave-contracts/contracts/test/MockE3Program.sol b/packages/enclave-contracts/contracts/test/MockE3Program.sol index 6356d483e..3c8d75e6f 100644 --- a/packages/enclave-contracts/contracts/test/MockE3Program.sol +++ b/packages/enclave-contracts/contracts/test/MockE3Program.sol @@ -50,6 +50,10 @@ contract MockE3Program is IE3Program { revert InvalidInput(); } if (address(enclave) != address(0)) { + // Test-only: external call to Enclave with no reentrancy guard. + // Deliberate — this contract is only deployed in integration tests + // and `enclave` is set via `setEnclave` to the trusted Enclave + // proxy. Do not copy this pattern into a production E3 program. // Pass `data` as the proof too so `MockE3Program.verify` (which // requires `proof.length > 0`) returns true. enclave.publishCiphertextOutput(e3Id, data, data); diff --git a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts index 82435c829..2672b43e0 100644 --- a/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvDecryptionVerifier.spec.ts @@ -77,6 +77,12 @@ function encodeProof(rawProof: string, publicInputs: string[]): string { return abiCoder.encode(["bytes", "bytes32[]"], [rawProof, publicInputs]); } +// `BfvDecryptionVerifier.verify` reverts with a granular custom error per +// failed check (`BadPublicInputsLen`, `BadC6FoldKeyHash`, `BadC7KeyHash`, +// `BadCommitteeHashHi/Lo`, `BadPlaintextHash`) instead of returning `false`, +// so failure modes are visible in the revert selector. The bool return is +// preserved only for the final Honk-verifier call, where `false` reflects +// the underlying circuit verifier's bool result. describe("BfvDecryptionVerifier", function () { const deployWithMockCircuit = async () => { const [owner] = await ethers.getSigners(); diff --git a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts index 28f75887e..3f43b8d33 100644 --- a/packages/enclave-contracts/test/BfvPkVerifier.spec.ts +++ b/packages/enclave-contracts/test/BfvPkVerifier.spec.ts @@ -50,6 +50,13 @@ function encodeProof(rawProof: string, publicInputs: string[]): string { return abiCoder.encode(["bytes", "bytes32[]"], [rawProof, publicInputs]); } +// `BfvPkVerifier.verify` reverts with a granular custom error per failed +// check (`BadPublicInputsLen`, `BadNodesFoldKeyHash`, `BadC5KeyHash`, +// `BadCommitteeHashHi/Lo`, `BadPkCommitment`) instead of returning `false`, +// so calls like `require(verify(...), InvalidDkgProof())` surface the +// specific mismatch rather than collapsing to a generic revert. The bool +// return is preserved only for the final Honk-verifier call, where `false` +// reflects the underlying circuit verifier's bool result. describe("BfvPkVerifier", function () { const deployWithMockCircuit = async () => { const [owner] = await ethers.getSigners(); From 29db97d86fbcf4b617aa6cd667f9e177bf191dc1 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 17:19:51 +0200 Subject: [PATCH 19/54] fix variable --- packages/enclave-contracts/scripts/deployAndSave/verifiers.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts b/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts index cfb7c6f1b..9232a2896 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/verifiers.ts @@ -170,7 +170,6 @@ export const deployAndSaveAllVerifiers = async ( hre: HardhatRuntimeEnvironment, ): Promise => { const contractNames = discoverVerifierContracts(); - const { ethers } = await hre.network.connect(); const chain = getDeploymentChain(hre); console.log(` Deploying to network: ${chain}`); From 271ed867a90f43207be9ac177efba248a56ad953 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 17:20:26 +0200 Subject: [PATCH 20/54] removed unused var --- packages/enclave-contracts/test/Enclave.spec.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 5524ba2a6..f7fcbc379 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -73,13 +73,6 @@ describe("Enclave", function () { "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", ); - /** ABI-encoded fake DKG proof for `MockPkVerifier` (last public input must equal `pkCommitment`). */ - const encodeMockDkgProof = (pkCommitment: string): string => - ethers.AbiCoder.defaultAbiCoder().encode( - ["bytes", "bytes32[]"], - ["0x", [pkCommitment]], - ); - /** Public inputs layout for `DkgFoldAttestationVerifier` with `h` honest parties. */ const encodeMockDkgProofForAttestation = ( pkCommitment: string, From 268b6a4c5e94fb9ed5d325ab0296d6de404fcd27 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 17:20:59 +0200 Subject: [PATCH 21/54] fix line length max --- .../enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol b/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol index 6d765fdbb..a0d406e61 100644 --- a/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol +++ b/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol @@ -18,7 +18,8 @@ import { * `crates/events/src/enclave_event/dkg_fold_attestation.rs`. */ library DkgFoldAttestationLib { - /// @dev `keccak256("DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)")` + /// @dev `keccak256("DkgFoldAttestation(uint256 chainId,uint256 e3Id, + /// uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)")` bytes32 public constant TYPEHASH = keccak256( "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)" From 3938a9897351a06c92329f34254fff809cee5189 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 17:44:17 +0200 Subject: [PATCH 22/54] cleanup after rebase --- crates/evm/src/ciphernode_registry_sol.rs | 4 ++-- crates/tests/tests/integration.rs | 14 +++++++----- .../registry/CiphernodeRegistryOwnable.sol | 3 --- .../verifiers/bfv/BfvDecryptionVerifier.sol | 12 ---------- .../dkgFoldAttestationVerifier.ts | 22 +++++++++---------- 5 files changed, 20 insertions(+), 35 deletions(-) diff --git a/crates/evm/src/ciphernode_registry_sol.rs b/crates/evm/src/ciphernode_registry_sol.rs index 6775d8047..9c91d8989 100644 --- a/crates/evm/src/ciphernode_registry_sol.rs +++ b/crates/evm/src/ciphernode_registry_sol.rs @@ -22,8 +22,8 @@ use anyhow::Result; use e3_events::{ prelude::*, AggregatorChanged, BusHandle, CommitteeFinalizeRequested, CommitteeFinalized, E3RequestComplete, E3id, EType, EffectsEnabled, EnclaveEvent, EnclaveEventData, - EventSubscriber, EventType, OrderedSet, Proof, PublicKeyAggregated, Seed, Shutdown, - TicketGenerated, TicketId, + EventSubscriber, EventType, Proof, PublicKeyAggregated, Seed, Shutdown, TicketGenerated, + TicketId, }; use e3_utils::{ArcBytes, NotifySync, MAILBOX_LIMIT}; use std::collections::HashMap; diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index b5ed5519b..2929616fb 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -2132,8 +2132,8 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { .send(TakeEvents::::new(28)) .await?; - let actual_pk_commitment_1 = match history.events.last().cloned().unwrap().into_data() { - e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev.pk_commitment, + let actual_pubkey_agg_1 = match history.events.last().cloned().unwrap().into_data() { + e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev, other => panic!("expected PublicKeyAggregated, got {other:?}"), }; assert_eq!( @@ -2142,7 +2142,8 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { pubkey: ArcBytes::from_bytes(&test_pubkey.to_bytes()), e3_id: E3id::new("1234", 1), nodes: OrderedSet::from(eth_addrs.clone()), - pk_commitment: actual_pk_commitment_1, + committee_addresses: actual_pubkey_agg_1.committee_addresses, + pk_commitment: actual_pubkey_agg_1.pk_commitment, dkg_aggregator_proof: None, dkg_attestation_bundle: None, } @@ -2174,8 +2175,8 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { .send(TakeEvents::::new(8)) .await?; - let actual_pk_commitment_2 = match history.events.last().cloned().unwrap().into_data() { - e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev.pk_commitment, + let actual_pubkey_agg_2 = match history.events.last().cloned().unwrap().into_data() { + e3_events::EnclaveEventData::PublicKeyAggregated(ev) => ev, other => panic!("expected PublicKeyAggregated, got {other:?}"), }; assert_eq!( @@ -2184,7 +2185,8 @@ async fn test_duplicate_e3_id_with_different_chain_id() -> Result<()> { pubkey: ArcBytes::from_bytes(&test_pubkey.to_bytes()), e3_id: E3id::new("1234", 2), nodes: OrderedSet::from(eth_addrs.clone()), - pk_commitment: actual_pk_commitment_2, + committee_addresses: actual_pubkey_agg_2.committee_addresses, + pk_commitment: actual_pubkey_agg_2.pk_commitment, dkg_aggregator_proof: None, dkg_attestation_bundle: None, } diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index f34e2a89a..342ddd33b 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -224,9 +224,6 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { bytes32 committeeHash = CommitteeHashLib.hash(c.topNodes); c.committeeHash = committeeHash; - bytes32 committeeHash = CommitteeHashLib.hash(c.topNodes); - c.committeeHash = committeeHash; - E3 memory e3 = enclave.getE3(e3Id); if (e3.proofAggregationEnabled) { _verifyAndStoreDkgAnchors( diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol index 6202f30aa..fe6b2689f 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/BfvDecryptionVerifier.sol @@ -55,18 +55,6 @@ contract BfvDecryptionVerifier is IDecryptionVerifier { /// @dev `4 + DEC_RETURN_PREFIX_LEN + DEC_RETURN_COLUMN_COUNT*(T+1) + MESSAGE_COEFFS_COUNT`. uint256 internal immutable expectedPublicInputsLen; - /// @dev `publicInputs` index for `committee_hash_hi` (after sub-circuit key hashes). - uint256 internal constant COMMITTEE_HASH_HI_IDX = 2; - - /// @dev `publicInputs` index for `committee_hash_lo`. - uint256 internal constant COMMITTEE_HASH_LO_IDX = 3; - - /// @notice BFV threshold `T`; must match the compiled DecryptionAggregator circuit. - uint256 public immutable threshold; - - /// @dev `4 + DEC_RETURN_PREFIX_LEN + DEC_RETURN_COLUMN_COUNT*(T+1) + MESSAGE_COEFFS_COUNT`. - uint256 internal immutable expectedPublicInputsLen; - /// @notice Underlying Honk verifier for the DecryptionAggregator circuit. ICircuitVerifier public immutable circuitVerifier; diff --git a/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts index 1e4f91c07..e7f3b9532 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts @@ -5,7 +5,10 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; -import type { DkgFoldAttestationVerifier } from "../../types"; +import { + DkgFoldAttestationVerifier, + DkgFoldAttestationVerifier__factory as DkgFoldAttestationVerifierFactory, +} from "../../types"; import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; export const deployAndSaveDkgFoldAttestationVerifier = async ( @@ -14,6 +17,7 @@ export const deployAndSaveDkgFoldAttestationVerifier = async ( dkgFoldAttestationVerifier: DkgFoldAttestationVerifier; }> => { const { ethers, networkName } = await hre.network.connect(); + const [signer] = await ethers.getSigners(); const chain = networkName ?? "localhost"; const existing = readDeploymentArgs("DkgFoldAttestationVerifier", chain); @@ -21,16 +25,13 @@ export const deployAndSaveDkgFoldAttestationVerifier = async ( console.log( ` DkgFoldAttestationVerifier already deployed at ${existing.address}`, ); - const dkgFoldAttestationVerifier = (await ethers.getContractAt( - "DkgFoldAttestationVerifier", - existing.address, - )) as DkgFoldAttestationVerifier; + const dkgFoldAttestationVerifier = + DkgFoldAttestationVerifierFactory.connect(existing.address, signer); return { dkgFoldAttestationVerifier }; } - const dkgFoldAttestationVerifier = await ethers.deployContract( - "DkgFoldAttestationVerifier", - ); + const dkgFoldAttestationVerifier = + await new DkgFoldAttestationVerifierFactory(signer).deploy(); await dkgFoldAttestationVerifier.waitForDeployment(); const address = await dkgFoldAttestationVerifier.getAddress(); const blockNumber = await ethers.provider.getBlockNumber(); @@ -42,8 +43,5 @@ export const deployAndSaveDkgFoldAttestationVerifier = async ( ); console.log(` DkgFoldAttestationVerifier deployed to: ${address}`); - return { - dkgFoldAttestationVerifier: - dkgFoldAttestationVerifier as DkgFoldAttestationVerifier, - }; + return { dkgFoldAttestationVerifier }; }; From daf74f2b8197565616fdff19a9cee9920a113873 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 17:57:54 +0200 Subject: [PATCH 23/54] small nits & fix --- .../enclave-contracts/deployed_contracts.json | 50 ++++++++++++++----- tests/integration/enclave.config.yaml | 12 ++--- tests/integration/output/.gitignore | 1 + 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/packages/enclave-contracts/deployed_contracts.json b/packages/enclave-contracts/deployed_contracts.json index fed693f62..dc1c65271 100644 --- a/packages/enclave-contracts/deployed_contracts.json +++ b/packages/enclave-contracts/deployed_contracts.json @@ -126,21 +126,21 @@ }, "localhost": { "PoseidonT3": { - "blockNumber": 5, + "blockNumber": 6, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 6, + "blockNumber": 7, "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 7, + "blockNumber": 8, "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveTicketToken": { @@ -149,14 +149,14 @@ "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 9, + "blockNumber": 10, "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }, "SlashingManager": { "constructorArgs": { "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 10, + "blockNumber": 11, "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" }, "CiphernodeRegistryOwnable": { @@ -171,7 +171,7 @@ "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" }, - "blockNumber": 11, + "blockNumber": 12, "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" }, "BondingRegistry": { @@ -193,7 +193,7 @@ "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" }, - "blockNumber": 12, + "blockNumber": 13, "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" }, "Enclave": { @@ -213,7 +213,7 @@ "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" }, - "blockNumber": 15, + "blockNumber": 16, "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "E3RefundManager": { @@ -229,24 +229,48 @@ "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" }, - "blockNumber": 17, + "blockNumber": 18, "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, "MockComputeProvider": { - "blockNumber": 19, + "blockNumber": 20, "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" }, "MockDecryptionVerifier": { - "blockNumber": 20, + "blockNumber": 21, "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" }, "MockPkVerifier": { - "blockNumber": 21, + "blockNumber": 22, "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" }, "MockE3Program": { - "blockNumber": 22, + "blockNumber": 23, "address": "0x851356ae760d987E095750cCeb3bC6014560891C" + }, + "ZKTranscriptLib": { + "blockNumber": 25, + "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" + }, + "DecryptionAggregatorVerifier": { + "blockNumber": 26, + "address": "0x998abeb3E57409262aE5b751f60747921B33613E" + }, + "DkgAggregatorVerifier": { + "blockNumber": 27, + "address": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49" + }, + "BfvDecryptionVerifier": { + "blockNumber": 28, + "address": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528" + }, + "BfvPkVerifier": { + "blockNumber": 30, + "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" + }, + "DkgFoldAttestationVerifier": { + "address": "0x9d4454B023096f34B160D6B654540c56A1F81688", + "blockNumber": 32 } } } \ No newline at end of file diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index b4e5d478f..e93ecf649 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -5,22 +5,22 @@ chains: contracts: e3_program: address: "0x851356ae760d987E095750cCeb3bC6014560891C" - deploy_block: 22 + deploy_block: 23 enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 15 + deploy_block: 16 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 11 + deploy_block: 12 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 12 + deploy_block: 13 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 10 + deploy_block: 11 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 6 + deploy_block: 7 program: dev: true diff --git a/tests/integration/output/.gitignore b/tests/integration/output/.gitignore index 936104340..d881a7485 100644 --- a/tests/integration/output/.gitignore +++ b/tests/integration/output/.gitignore @@ -2,3 +2,4 @@ plaintext.txt *.hex *.db +events.txt From 369bf6dcfead1b5945394e493f89b03436de9107 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 18:06:34 +0200 Subject: [PATCH 24/54] update ABI --- .../contracts/Enclave.sol/Enclave.json | 2 +- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 76 ++++++++++- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../ISlashingManager.json | 2 +- .../CiphernodeRegistryOwnable.json | 126 ++++++++++++++++-- .../EnclaveTicketToken.json | 18 +-- 7 files changed, 201 insertions(+), 27 deletions(-) diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 4eec94d7b..cadedd249 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -2564,5 +2564,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-0431dc09dff33382cec136e860cfd8d32d55db8a" + "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 8b843cf8d..432544d0d 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -1005,5 +1005,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" + "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 59d3b845d..558b2202c 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -3,6 +3,11 @@ "contractName": "ICiphernodeRegistry", "sourceName": "contracts/interfaces/ICiphernodeRegistry.sol", "abi": [ + { + "inputs": [], + "name": "AttestationBindingCountMismatch", + "type": "error" + }, { "inputs": [], "name": "BondingRegistryNotSet", @@ -59,6 +64,21 @@ "name": "CommitteeNotRequested", "type": "error" }, + { + "inputs": [], + "name": "DkgProofRequired", + "type": "error" + }, + { + "inputs": [], + "name": "FoldAttestationVerifierNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "FoldAttestationsRequired", + "type": "error" + }, { "inputs": [ { @@ -75,6 +95,16 @@ "name": "InsufficientCiphernodes", "type": "error" }, + { + "inputs": [], + "name": "InvalidDkgProof", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidFoldAttestation", + "type": "error" + }, { "inputs": [], "name": "InvalidTicketNumber", @@ -126,6 +156,16 @@ "name": "OnlyEnclave", "type": "error" }, + { + "inputs": [], + "name": "PartyIdNotInProof", + "type": "error" + }, + { + "inputs": [], + "name": "PkCommitmentRequired", + "type": "error" + }, { "inputs": [], "name": "SubmissionWindowClosed", @@ -688,6 +728,35 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "getDkgAnchors", + "outputs": [ + { + "internalType": "uint256[]", + "name": "partyIds", + "type": "uint256[]" + }, + { + "internalType": "bytes32[]", + "name": "skAggCommits", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "esmAggCommits", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -814,6 +883,11 @@ "internalType": "bytes", "name": "proof", "type": "bytes" + }, + { + "internalType": "bytes", + "name": "dkgAttestationBundle", + "type": "bytes" } ], "name": "publishCommittee", @@ -985,5 +1059,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" + "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index 28718e065..6578b63b7 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2097,5 +2097,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" + "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index bfc1587e1..3a73fe649 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -1006,5 +1006,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-e33d1cfedc69d8ad74eec5a84c8ef358a020ec1a" + "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index 50424a311..07d9c8eaa 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -8,6 +8,11 @@ "stateMutability": "nonpayable", "type": "constructor" }, + { + "inputs": [], + "name": "AttestationBindingCountMismatch", + "type": "error" + }, { "inputs": [], "name": "BondingRegistryNotSet", @@ -64,6 +69,21 @@ "name": "CommitteeNotRequested", "type": "error" }, + { + "inputs": [], + "name": "DkgProofRequired", + "type": "error" + }, + { + "inputs": [], + "name": "FoldAttestationVerifierNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "FoldAttestationsRequired", + "type": "error" + }, { "inputs": [ { @@ -80,6 +100,16 @@ "name": "InsufficientCiphernodes", "type": "error" }, + { + "inputs": [], + "name": "InvalidDkgProof", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidFoldAttestation", + "type": "error" + }, { "inputs": [], "name": "InvalidInitialization", @@ -163,6 +193,16 @@ "name": "OwnableUnauthorizedAccount", "type": "error" }, + { + "inputs": [], + "name": "PartyIdNotInProof", + "type": "error" + }, + { + "inputs": [], + "name": "PkCommitmentRequired", + "type": "error" + }, { "inputs": [], "name": "SubmissionWindowClosed", @@ -684,6 +724,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "dkgFoldAttestationVerifier", + "outputs": [ + { + "internalType": "contract IDkgFoldAttestationVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "enclave", @@ -878,6 +931,35 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + } + ], + "name": "getDkgAnchors", + "outputs": [ + { + "internalType": "uint256[]", + "name": "partyIds", + "type": "uint256[]" + }, + { + "internalType": "bytes32[]", + "name": "skAggCommits", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "esmAggCommits", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1067,6 +1149,11 @@ "internalType": "bytes", "name": "proof", "type": "bytes" + }, + { + "internalType": "bytes", + "name": "dkgAttestationBundle", + "type": "bytes" } ], "name": "publishCommittee", @@ -1187,6 +1274,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract IDkgFoldAttestationVerifier", + "name": "verifier", + "type": "address" + } + ], + "name": "setDkgFoldAttestationVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1297,30 +1397,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b613e01806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061024a575f3560e01c80639a7a2ffc11610140578063da881e5a116100bf578063ebf0c71711610084578063ebf0c7171461058a578063f165053614610592578063f2fde38b146105ac578063f379b0df146105bf578063f52fd803146105f9578063f6fc05d514610669575f5ffd5b8063da881e5a1461052c578063dbb06c931461053f578063e59e469514610551578063e6745e1314610564578063e82f3b7014610577575f5ffd5b8063c2b40ae411610105578063c2b40ae4146104b7578063c3a0ec30146104d6578063c6b2a438146104e7578063ca2869a0146104fa578063cd6dc68714610519575f5ffd5b80639a7a2ffc1461042c5780639f0f874a14610468578063a016493014610471578063a8a4d69b14610491578063bff232c1146104a4575f5ffd5b806370e36bbe116101cc5780638cb89ecb116101915780638cb89ecb146103c95780638d1ddfb1146103e85780638da5cb5b146103fe5780638e5ce3ad146104065780639015d37114610419575f5ffd5b806370e36bbe1461034e578063715018a6146103615780637c92f5241461036957806385814243146103965780638a78bb15146103b6575f5ffd5b8063291a691b11610212578063291a691b146102d05780632e7b716d146102f35780634d6861a61461030657806350e6d94c146103195780635d5047761461033b575f5ffd5b8063096b810a1461024e578063099a161a146102635780630f3e34121461028957806317d611201461029c5780632800d829146102bd575b5f5ffd5b61026161025c3660046133b3565b610672565b005b6102766102713660046133ce565b6107be565b6040519081526020015b60405180910390f35b6102616102973660046133ce565b6107f7565b6102af6102aa3660046133ce565b61083a565b604051610280929190613458565b6102766102cb3660046133ce565b6109e3565b6102e36102de366004613485565b610a2f565b6040519015158152602001610280565b6102e36103013660046133b3565b610c09565b6102e36103143660046133ce565b610cbc565b6102e36103273660046133b3565b60066020525f908152604090205460ff1681565b6102e36103493660046134be565b610cfb565b61026161035c3660046133b3565b610d3e565b610261610db4565b61037c6103773660046134ec565b610dc7565b6040805192835263ffffffff909116602083015201610280565b6001546103a9906001600160a01b031681565b6040516102809190613521565b6102616103c43660046133b3565b610f6e565b6102766103d73660046133ce565b60096020525f908152604090205481565b600454600160281b900464ffffffffff16610276565b6103a96110ac565b600b546103a9906001600160a01b031681565b6102e36104273660046133b3565b6110da565b61045261043a3660046133b3565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff9091168152602001610280565b61027660035481565b61048461047f3660046133ce565b6110f7565b6040516102809190613535565b6102e361049f3660046134be565b61118d565b6102616104b23660046133b3565b6111d0565b6102766104c53660046133ce565b60086020525f908152604090205481565b6001546001600160a01b03166103a9565b6102616104f536600461358b565b611248565b6102766105083660046133ce565b5f9081526008602052604090205490565b61026161052736600461360b565b61153e565b6102e361053a3660046133ce565b61169b565b5f546103a9906001600160a01b031681565b61026161055f3660046133b3565b611975565b610261610572366004613635565b6119ed565b6102766105853660046133ce565b611bb0565b610276611be1565b61059a601481565b60405160ff9091168152602001610280565b6102616105ba3660046133b3565b611bf3565b6004546105db9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff938416815292909116602083015201610280565b61063a6106073660046133ce565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b604051610280949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61027660025481565b61067a6110ac565b6001600160a01b0316336001600160a01b031614806106a357506001546001600160a01b031633145b6106c057604051632864c4e160e01b815260040160405180910390fd5b6106c9816110da565b81906106f2576040516381e5828960e01b81526004016106e99190613521565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107209060049083611c2d565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161074d83613669565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a6020526040812060048101546107ed576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b6107ff611ecf565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b038111156108705761087061367e565b604051908082528060200260200182016040528015610899578160200160208202803683370190505b509450806001600160401b038111156108b4576108b461367e565b6040519080825280602002602001820160405280156108dd578160200160208202803683370190505b5093505f805b838110156109d9575f85600601828154811061090157610901613692565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610947576109476136a6565b036109d0578088848151811061095f5761095f613692565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109b7576109b7613692565b6020908102919091010152826109cc816136ba565b9350505b506001016108e3565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610a0757610a076136a6565b03610a2557604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610a5a5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610a7e57610a7e6136a6565b14610a9c576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610ae3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b0791906136d2565b905080610b1a60408601602087016136fc565b63ffffffff161115610b3260408601602087016136fc565b829091610b60576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106e9565b5050815460ff1916600190811783558201859055436002830155600354610b879042613715565b6003830155610b9b600583018560026132ea565b50610ba4611be1565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610bf5928a928a9291613728565b60405180910390a250600195945050505050565b5f610c13826110da565b610c1e57505f919050565b6001546001600160a01b0316610c47576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610c77908590600401613521565b602060405180830381865afa158015610c92573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb69190613787565b92915050565b5f818152600a602052604081206001815460ff166003811115610ce157610ce16136a6565b14610cee57505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115610d3657610d366136a6565b149392505050565b610d46611ecf565b6001600160a01b038116610d6d5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610dbc611ecf565b610dc55f611f01565b565b600b545f9081906001600160a01b03163314610df65760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610e1b57610e1b6136a6565b14610e3957604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff166002811115610e7957610e796136a6565b14610e8957600b01549150610f66565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b8201805491610ebd83613669565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610f0e929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610f766110ac565b6001600160a01b0316336001600160a01b03161480610f9f57506001546001600160a01b031633145b610fbc57604051632864c4e160e01b815260040160405180910390fd5b610fc5816110da565b6110a95760048054600160281b900464ffffffffff1690610fef906001600160a01b038416611f71565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491611040836136ba565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016107b2565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a6020526040902060048101546060919061112a576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561118057602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611162575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156111c7576111c76136a6565b14159392505050565b6111d8611ecf565b6001600160a01b0381166111ff5760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f868152600a602052604090206002815460ff16600381111561126d5761126d6136a6565b1461128b57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156112ae5760405163632a22bb60e01b815260040160405180910390fd5b836112f35760405162461bcd60e51b81526020600482015260156024820152741c1ad0dbdb5b5a5d1b595b9d081c995c5d5a5c9959605a1b60448201526064016106e9565b5f61130082600601612147565b600783018190555f805460405163101bb4d760e21b8152600481018c905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015611352573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261137991908101906138e7565b9050806101c001511561147a57836113c45760405162461bcd60e51b815260206004820152600e60248201526d1c1c9bdbd9881c995c5d5a5c995960921b60448201526064016106e9565b8061012001516001600160a01b031663de12c640878488886040518563ffffffff1660e01b81526004016113fb9493929190613a6d565b602060405180830381865afa158015611416573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061143a9190613787565b61147a5760405162461bcd60e51b815260206004820152601160248201527024b73b30b634b2102225a390383937b7b360791b60448201526064016106e9565b60048381018790555f8a815260096020526040808220899055905490516340a3b76160e11b81529182018b9052602482018890526001600160a01b0316906381476ec2906044015f604051808303815f87803b1580156114d8575f5ffd5b505af11580156114ea573d5f5f3e3d5ffd5b50505050887fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018a8a8a8a8a60405161152b96959493929190613ad2565b60405180910390a2505050505050505050565b5f6115476121a8565b805490915060ff600160401b82041615906001600160401b03165f8115801561156d5750825b90505f826001600160401b031660011480156115885750303b155b905081158015611596575080155b156115b45760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156115de57845460ff60401b1916600160401b1785555b6001600160a01b0387166116055760405163d92e233d60e01b815260040160405180910390fd5b61160e336121d0565b61161a600460146121e1565b611623866107f7565b61162b6110ac565b6001600160a01b0316876001600160a01b03161461164c5761164c87611bf3565b831561169257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156116bf576116bf6136a6565b036116dd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116f5576116f56136a6565b1461171357604051631860f69960e31b815260040160405180910390fd5b8060030154421161173757604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061181c578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b1580156117fd575f5ffd5b505af115801561180f573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600b83018190555f816001600160401b0381111561184c5761184c61367e565b604051908082528060200260200182016040528015611875578160200160208202803683370190505b5090505f5b828110156118e757846009015f86600601838154811061189c5761189c613692565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106118d4576118d4613692565b602090810291909101015260010161187a565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b15801561192a575f5ffd5b505af115801561193c573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610bf5929190613b1f565b61197d611ecf565b6001600160a01b0381166119a45760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611a1157611a116136a6565b03611a2f57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611a4757611a476136a6565b14611a6557604051631860f69960e31b815260040160405180910390fd5b8060030154421115611a8a57604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611abc5760405163257309f160e11b815260040160405180910390fd5b611ac533610c09565b611ae25760405163149fbcfd60e11b815260040160405180910390fd5b611aed338385612260565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611b6c90839083612431565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611bdc576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611bee60046014612632565b905090565b611bfb611ecf565b6001600160a01b038116611c24575f604051631e4fbdf760e01b81526004016106e99190613521565b6110a981611f01565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611c6c5760405162461bcd60e51b81526004016106e990613b31565b825464ffffffffff600160281b90910481169082168111611cca5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106e9565b825f5b81866001015f611cdd848861272b565b64ffffffffff1681526020019081526020015f20819055505f816001611d039190613b7b565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611d385750611ec7565b600185165f03611dff575f611d5783611d52886001613b94565b61272b565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611db891600401613bb1565b602060405180830381865af4158015611dd3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611df791906136d2565b935050611eb3565b5f611e0f83611d52600189613be1565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e7091600401613bb1565b602060405180830381865af4158015611e8b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611eaf91906136d2565b9350505b50647fffffffff600194851c169301611ccd565b505050505050565b33611ed86110ac565b6001600160a01b031614610dc5573360405163118cdaa760e01b81526004016106e99190613521565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611fc05760405162461bcd60e51b81526004016106e990613b31565b825464ffffffffff908116908216106120135760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106e9565b61201e816001613b94565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f612055848761272b565b64ffffffffff16815260208101919091526040015f20556001831615612140575f61208582611d52600187613be1565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916120e691600401613bb1565b602060405180830381865af4158015612101573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212591906136d2565b647fffffffff600195861c1694909350919091019050612045565b5050505050565b5f610cb68280548060200260200160405190810160405280929190818152602001828054801561219e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612180575b5050505050612748565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610cb6565b6121d8612777565b6110a98161279c565b602060ff8216111561222f5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106e9565b612240600160ff831681901b613bfe565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116122805760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166122a9576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd719188916122df91613bfe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612326573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061234a91906136d2565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561239d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123c191906136d2565b90505f81116123e35760405163aeaddff160e01b815260040160405180910390fd5b5f6123ee8284613c11565b90505f81116124105760405163149fbcfd60e11b815260040160405180910390fd5b808611156116925760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156124af57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff191682179055905061262b565b5f5f90505f876009015f855f815481106124cb576124cb613692565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612553575f896009015f87848154811061251557612515613692565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561254a578092508193505b506001016124f4565b50808610612567575f94505050505061262b565b5f88600a015f86858154811061257f5761257f613692565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125bc576125bc6136a6565b0217905550868483815481106125d4576125d4613692565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116126855760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106e9565b602060ff831611156126a95760405162461bcd60e51b81526004016106e990613c30565b8254600160281b900464ffffffffff16806126c860ff85166002613d81565b64ffffffffff1610156127185760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106e9565b6127238482856127a4565b949350505050565b5f8161273e60ff851663ffffffff613d9a565b61262b9190613b94565b5f8160405160200161275a9190613dc1565b604051602081830303815290604052805190602001209050919050565b61277f61286c565b610dc557604051631afcd79f60e31b815260040160405180910390fd5b611bfb612777565b5f602060ff831611156127c95760405162461bcd60e51b81526004016106e990613c30565b8264ffffffffff165f036127e7576127e082612885565b905061262b565b5f6127f3836001613b7b565b60ff166001600160401b0381111561280d5761280d61367e565b604051908082528060200260200182016040528015612836578160200160208202803683370190505b50905061284585858584612f1f565b808360ff168151811061285a5761285a613692565b60200260200101519150509392505050565b5f6128756121a8565b54600160401b900460ff16919050565b5f8160ff165f0361289757505f919050565b8160ff166001036128c957507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036128fb57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361292d57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361295f57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361299157507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036129c357507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036129f557507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612a2757507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612a5957507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612a8b57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612abd57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612aef57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612b2157507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612b5357507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612b8557507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612bb757507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612be957507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612c1b57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612c4d57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612c7f57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612cb157507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612ce357507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612d1557507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612d4757507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612d7957507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612dab57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612ddd57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612e0f57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612e4157507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612e7357507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612ea557507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612ed757507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106e9565b602060ff83161115612f435760405162461bcd60e51b81526004016106e990613c30565b5f8364ffffffffff1611612fa75760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106e9565b5f612fb3600185613be1565b9050600181165f0361300657846001015f612fce5f8461272b565b64ffffffffff1681526020019081526020015f2054825f81518110612ff557612ff5613692565b60200260200101818152505061302e565b61300f5f612885565b825f8151811061302157613021613692565b6020026020010181815250505b5f5b8360ff168160ff161015611ec757600182165f036131265773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061308257613082613692565b6020026020010151815260200161309885612885565b8152506040518263ffffffff1660e01b81526004016130b79190613bb1565b602060405180830381865af41580156130d2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130f691906136d2565b83613102836001613b7b565b60ff168151811061311557613115613692565b6020026020010181815250506132d7565b5f613132826001613b7b565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156131d4575f876001015f6131898560016131789190613b7b565b60018864ffffffffff16901c61272b565b64ffffffffff1681526020019081526020015f2054905080858460016131af9190613b7b565b60ff16815181106131c2576131c2613692565b602002602001018181525050506132d5565b5f876001015f6131eb85600188611d529190613be1565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061324257613242613692565b60200260200101518152506040518263ffffffff1660e01b81526004016132699190613bb1565b602060405180830381865af4158015613284573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132a891906136d2565b856132b4856001613b7b565b60ff16815181106132c7576132c7613692565b602002602001018181525050505b505b647fffffffff600192831c169101613030565b60018301918390821561337b579160200282015f5b8382111561334957833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026132ff565b80156133795782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613349565b505b5061338792915061338b565b5090565b5b80821115613387575f815560010161338c565b6001600160a01b03811681146110a9575f5ffd5b5f602082840312156133c3575f5ffd5b813561262b8161339f565b5f602082840312156133de575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b8281101561341e5781516001600160a01b03168652602095860195909101906001016133f7565b5093949350505050565b5f8151808452602084019350602083015f5b8281101561341e57815186526020958601959091019060010161343a565b604081525f61346a60408301856133e5565b828103602084015261347c8185613428565b95945050505050565b5f5f5f60808486031215613497575f5ffd5b8335925060208401359150608084018510156134b1575f5ffd5b6040840190509250925092565b5f5f604083850312156134cf575f5ffd5b8235915060208301356134e18161339f565b809150509250929050565b5f5f5f606084860312156134fe575f5ffd5b8335925060208401356135108161339f565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f61262b60208301846133e5565b5f5f83601f840112613557575f5ffd5b5081356001600160401b0381111561356d575f5ffd5b602083019150836020828501011115613584575f5ffd5b9250929050565b5f5f5f5f5f5f608087890312156135a0575f5ffd5b8635955060208701356001600160401b038111156135bc575f5ffd5b6135c889828a01613547565b9096509450506040870135925060608701356001600160401b038111156135ed575f5ffd5b6135f989828a01613547565b979a9699509497509295939492505050565b5f5f6040838503121561361c575f5ffd5b82356136278161339f565b946020939093013593505050565b5f5f60408385031215613646575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161367757613677613655565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f600182016136cb576136cb613655565b5060010190565b5f602082840312156136e2575f5ffd5b5051919050565b803563ffffffff81168114611bdc575f5ffd5b5f6020828403121561370c575f5ffd5b61262b826136e9565b80820180821115610cb657610cb6613655565b84815260a0810160208201855f5b60028110156137635763ffffffff61374d836136e9565b1683526020928301929190910190600101613736565b50505060608201939093526080015292915050565b80518015158114611bdc575f5ffd5b5f60208284031215613797575f5ffd5b61262b82613778565b6040516101e081016001600160401b03811182821017156137c3576137c361367e565b60405290565b805160048110611bdc575f5ffd5b5f82601f8301126137e6575f5ffd5b604080519081016001600160401b03811182821017156138085761380861367e565b806040525080604084018581111561381e575f5ffd5b845b81811015613838578051835260209283019201613820565b509195945050505050565b8051611bdc8161339f565b805160ff81168114611bdc575f5ffd5b5f82601f83011261386d575f5ffd5b81516001600160401b038111156138865761388661367e565b604051601f8201601f19908116603f011681016001600160401b03811182821017156138b4576138b461367e565b6040528181528382016020018510156138cb575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f602082840312156138f7575f5ffd5b81516001600160401b0381111561390c575f5ffd5b8201610200818503121561391e575f5ffd5b6139266137a0565b81518152613936602083016137c9565b60208201526040828101519082015261395285606084016137d7565b606082015260a0820151608082015261396d60c08301613843565b60a082015261397e60e0830161384e565b60c08201526101008201516001600160401b0381111561399c575f5ffd5b6139a88682850161385e565b60e0830152506139bb6101208301613843565b6101008201526139ce6101408301613843565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a04575f5ffd5b613a108682850161385e565b61018083015250613a246101c08301613843565b6101a0820152613a376101e08301613778565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b848152836020820152606060408201525f613a8c606083018486613a45565b9695505050505050565b5f8154808452602084019350825f5260205f205f5b8281101561341e5781546001600160a01b0316865260209095019460019182019101613aab565b608081525f613ae46080830189613a96565b8281036020840152613af781888a613a45565b90508560408401528281036060840152613b12818587613a45565b9998505050505050505050565b604081525f61346a6040830185613a96565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610cb657610cb6613655565b64ffffffffff8181168382160190811115610cb657610cb6613655565b6040810181835f5b6002811015613bd8578151835260209283019290910190600101613bb9565b50505092915050565b64ffffffffff8281168282160390811115610cb657610cb6613655565b81810381811115610cb657610cb6613655565b5f82613c2b57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610f6657808504811115613c9257613c92613655565b6001841615613ca057908102905b60019390931c928002613c77565b5f82613cbc57506001610cb6565b81613cc857505f610cb6565b8160018114613cde5760028114613ce857613d1a565b6001915050610cb6565b60ff841115613cf957613cf9613655565b6001841b915064ffffffffff821115613d1457613d14613655565b50610cb6565b5060208310610133831016604e8410600b8410161715613d52575081810a64ffffffffff811115613d4d57613d4d613655565b610cb6565b613d6264ffffffffff8484613c73565b8064ffffffffff04821115613d7957613d79613655565b029392505050565b5f61262b64ffffffffff841664ffffffffff8416613cae565b64ffffffffff8181168382160290811690818114613dba57613dba613655565b5092915050565b81515f90829060208501835b828110156138385781516001600160a01b0316845260209384019390910190600101613dcd56fea164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061024a575f3560e01c80639a7a2ffc11610140578063da881e5a116100bf578063ebf0c71711610084578063ebf0c7171461058a578063f165053614610592578063f2fde38b146105ac578063f379b0df146105bf578063f52fd803146105f9578063f6fc05d514610669575f5ffd5b8063da881e5a1461052c578063dbb06c931461053f578063e59e469514610551578063e6745e1314610564578063e82f3b7014610577575f5ffd5b8063c2b40ae411610105578063c2b40ae4146104b7578063c3a0ec30146104d6578063c6b2a438146104e7578063ca2869a0146104fa578063cd6dc68714610519575f5ffd5b80639a7a2ffc1461042c5780639f0f874a14610468578063a016493014610471578063a8a4d69b14610491578063bff232c1146104a4575f5ffd5b806370e36bbe116101cc5780638cb89ecb116101915780638cb89ecb146103c95780638d1ddfb1146103e85780638da5cb5b146103fe5780638e5ce3ad146104065780639015d37114610419575f5ffd5b806370e36bbe1461034e578063715018a6146103615780637c92f5241461036957806385814243146103965780638a78bb15146103b6575f5ffd5b8063291a691b11610212578063291a691b146102d05780632e7b716d146102f35780634d6861a61461030657806350e6d94c146103195780635d5047761461033b575f5ffd5b8063096b810a1461024e578063099a161a146102635780630f3e34121461028957806317d611201461029c5780632800d829146102bd575b5f5ffd5b61026161025c3660046133b3565b610672565b005b6102766102713660046133ce565b6107be565b6040519081526020015b60405180910390f35b6102616102973660046133ce565b6107f7565b6102af6102aa3660046133ce565b61083a565b604051610280929190613458565b6102766102cb3660046133ce565b6109e3565b6102e36102de366004613485565b610a2f565b6040519015158152602001610280565b6102e36103013660046133b3565b610c09565b6102e36103143660046133ce565b610cbc565b6102e36103273660046133b3565b60066020525f908152604090205460ff1681565b6102e36103493660046134be565b610cfb565b61026161035c3660046133b3565b610d3e565b610261610db4565b61037c6103773660046134ec565b610dc7565b6040805192835263ffffffff909116602083015201610280565b6001546103a9906001600160a01b031681565b6040516102809190613521565b6102616103c43660046133b3565b610f6e565b6102766103d73660046133ce565b60096020525f908152604090205481565b600454600160281b900464ffffffffff16610276565b6103a96110ac565b600b546103a9906001600160a01b031681565b6102e36104273660046133b3565b6110da565b61045261043a3660046133b3565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff9091168152602001610280565b61027660035481565b61048461047f3660046133ce565b6110f7565b6040516102809190613535565b6102e361049f3660046134be565b61118d565b6102616104b23660046133b3565b6111d0565b6102766104c53660046133ce565b60086020525f908152604090205481565b6001546001600160a01b03166103a9565b6102616104f536600461358b565b611248565b6102766105083660046133ce565b5f9081526008602052604090205490565b61026161052736600461360b565b61153e565b6102e361053a3660046133ce565b61169b565b5f546103a9906001600160a01b031681565b61026161055f3660046133b3565b611975565b610261610572366004613635565b6119ed565b6102766105853660046133ce565b611bb0565b610276611be1565b61059a601481565b60405160ff9091168152602001610280565b6102616105ba3660046133b3565b611bf3565b6004546105db9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff938416815292909116602083015201610280565b61063a6106073660046133ce565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b604051610280949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61027660025481565b61067a6110ac565b6001600160a01b0316336001600160a01b031614806106a357506001546001600160a01b031633145b6106c057604051632864c4e160e01b815260040160405180910390fd5b6106c9816110da565b81906106f2576040516381e5828960e01b81526004016106e99190613521565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107209060049083611c2d565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161074d83613669565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a6020526040812060048101546107ed576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b6107ff611ecf565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b038111156108705761087061367e565b604051908082528060200260200182016040528015610899578160200160208202803683370190505b509450806001600160401b038111156108b4576108b461367e565b6040519080825280602002602001820160405280156108dd578160200160208202803683370190505b5093505f805b838110156109d9575f85600601828154811061090157610901613692565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610947576109476136a6565b036109d0578088848151811061095f5761095f613692565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109b7576109b7613692565b6020908102919091010152826109cc816136ba565b9350505b506001016108e3565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610a0757610a076136a6565b03610a2557604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610a5a5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610a7e57610a7e6136a6565b14610a9c576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610ae3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b0791906136d2565b905080610b1a60408601602087016136fc565b63ffffffff161115610b3260408601602087016136fc565b829091610b60576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106e9565b5050815460ff1916600190811783558201859055436002830155600354610b879042613715565b6003830155610b9b600583018560026132ea565b50610ba4611be1565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610bf5928a928a9291613728565b60405180910390a250600195945050505050565b5f610c13826110da565b610c1e57505f919050565b6001546001600160a01b0316610c47576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610c77908590600401613521565b602060405180830381865afa158015610c92573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cb69190613787565b92915050565b5f818152600a602052604081206001815460ff166003811115610ce157610ce16136a6565b14610cee57505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115610d3657610d366136a6565b149392505050565b610d46611ecf565b6001600160a01b038116610d6d5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610dbc611ecf565b610dc55f611f01565b565b600b545f9081906001600160a01b03163314610df65760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610e1b57610e1b6136a6565b14610e3957604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff166002811115610e7957610e796136a6565b14610e8957600b01549150610f66565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b8201805491610ebd83613669565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610f0e929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610f766110ac565b6001600160a01b0316336001600160a01b03161480610f9f57506001546001600160a01b031633145b610fbc57604051632864c4e160e01b815260040160405180910390fd5b610fc5816110da565b6110a95760048054600160281b900464ffffffffff1690610fef906001600160a01b038416611f71565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491611040836136ba565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016107b2565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a6020526040902060048101546060919061112a576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561118057602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611162575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156111c7576111c76136a6565b14159392505050565b6111d8611ecf565b6001600160a01b0381166111ff5760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f868152600a602052604090206002815460ff16600381111561126d5761126d6136a6565b1461128b57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156112ae5760405163632a22bb60e01b815260040160405180910390fd5b836112f35760405162461bcd60e51b81526020600482015260156024820152741c1ad0dbdb5b5a5d1b595b9d081c995c5d5a5c9959605a1b60448201526064016106e9565b5f61130082600601612147565b600783018190555f805460405163101bb4d760e21b8152600481018c905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015611352573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261137991908101906138e7565b9050806101c001511561147a57836113c45760405162461bcd60e51b815260206004820152600e60248201526d1c1c9bdbd9881c995c5d5a5c995960921b60448201526064016106e9565b8061012001516001600160a01b031663de12c640878488886040518563ffffffff1660e01b81526004016113fb9493929190613a6d565b602060405180830381865afa158015611416573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061143a9190613787565b61147a5760405162461bcd60e51b815260206004820152601160248201527024b73b30b634b2102225a390383937b7b360791b60448201526064016106e9565b60048381018790555f8a815260096020526040808220899055905490516340a3b76160e11b81529182018b9052602482018890526001600160a01b0316906381476ec2906044015f604051808303815f87803b1580156114d8575f5ffd5b505af11580156114ea573d5f5f3e3d5ffd5b50505050887fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018a8a8a8a8a60405161152b96959493929190613ad2565b60405180910390a2505050505050505050565b5f6115476121a8565b805490915060ff600160401b82041615906001600160401b03165f8115801561156d5750825b90505f826001600160401b031660011480156115885750303b155b905081158015611596575080155b156115b45760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156115de57845460ff60401b1916600160401b1785555b6001600160a01b0387166116055760405163d92e233d60e01b815260040160405180910390fd5b61160e336121d0565b61161a600460146121e1565b611623866107f7565b61162b6110ac565b6001600160a01b0316876001600160a01b03161461164c5761164c87611bf3565b831561169257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156116bf576116bf6136a6565b036116dd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116f5576116f56136a6565b1461171357604051631860f69960e31b815260040160405180910390fd5b8060030154421161173757604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061181c578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b1580156117fd575f5ffd5b505af115801561180f573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600b83018190555f816001600160401b0381111561184c5761184c61367e565b604051908082528060200260200182016040528015611875578160200160208202803683370190505b5090505f5b828110156118e757846009015f86600601838154811061189c5761189c613692565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106118d4576118d4613692565b602090810291909101015260010161187a565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b15801561192a575f5ffd5b505af115801561193c573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610bf5929190613b1f565b61197d611ecf565b6001600160a01b0381166119a45760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611a1157611a116136a6565b03611a2f57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611a4757611a476136a6565b14611a6557604051631860f69960e31b815260040160405180910390fd5b8060030154421115611a8a57604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611abc5760405163257309f160e11b815260040160405180910390fd5b611ac533610c09565b611ae25760405163149fbcfd60e11b815260040160405180910390fd5b611aed338385612260565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611b6c90839083612431565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611bdc576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611bee60046014612632565b905090565b611bfb611ecf565b6001600160a01b038116611c24575f604051631e4fbdf760e01b81526004016106e99190613521565b6110a981611f01565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611c6c5760405162461bcd60e51b81526004016106e990613b31565b825464ffffffffff600160281b90910481169082168111611cca5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106e9565b825f5b81866001015f611cdd848861272b565b64ffffffffff1681526020019081526020015f20819055505f816001611d039190613b7b565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611d385750611ec7565b600185165f03611dff575f611d5783611d52886001613b94565b61272b565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611db891600401613bb1565b602060405180830381865af4158015611dd3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611df791906136d2565b935050611eb3565b5f611e0f83611d52600189613be1565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e7091600401613bb1565b602060405180830381865af4158015611e8b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611eaf91906136d2565b9350505b50647fffffffff600194851c169301611ccd565b505050505050565b33611ed86110ac565b6001600160a01b031614610dc5573360405163118cdaa760e01b81526004016106e99190613521565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611fc05760405162461bcd60e51b81526004016106e990613b31565b825464ffffffffff908116908216106120135760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106e9565b61201e816001613b94565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f612055848761272b565b64ffffffffff16815260208101919091526040015f20556001831615612140575f61208582611d52600187613be1565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916120e691600401613bb1565b602060405180830381865af4158015612101573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061212591906136d2565b647fffffffff600195861c1694909350919091019050612045565b5050505050565b5f610cb68280548060200260200160405190810160405280929190818152602001828054801561219e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612180575b5050505050612748565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610cb6565b6121d8612777565b6110a98161279c565b602060ff8216111561222f5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106e9565b612240600160ff831681901b613bfe565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116122805760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166122a9576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd719188916122df91613bfe565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612326573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061234a91906136d2565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561239d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123c191906136d2565b90505f81116123e35760405163aeaddff160e01b815260040160405180910390fd5b5f6123ee8284613c11565b90505f81116124105760405163149fbcfd60e11b815260040160405180910390fd5b808611156116925760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156124af57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff191682179055905061262b565b5f5f90505f876009015f855f815481106124cb576124cb613692565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612553575f896009015f87848154811061251557612515613692565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561254a578092508193505b506001016124f4565b50808610612567575f94505050505061262b565b5f88600a015f86858154811061257f5761257f613692565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125bc576125bc6136a6565b0217905550868483815481106125d4576125d4613692565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116126855760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106e9565b602060ff831611156126a95760405162461bcd60e51b81526004016106e990613c30565b8254600160281b900464ffffffffff16806126c860ff85166002613d81565b64ffffffffff1610156127185760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106e9565b6127238482856127a4565b949350505050565b5f8161273e60ff851663ffffffff613d9a565b61262b9190613b94565b5f8160405160200161275a9190613dc1565b604051602081830303815290604052805190602001209050919050565b61277f61286c565b610dc557604051631afcd79f60e31b815260040160405180910390fd5b611bfb612777565b5f602060ff831611156127c95760405162461bcd60e51b81526004016106e990613c30565b8264ffffffffff165f036127e7576127e082612885565b905061262b565b5f6127f3836001613b7b565b60ff166001600160401b0381111561280d5761280d61367e565b604051908082528060200260200182016040528015612836578160200160208202803683370190505b50905061284585858584612f1f565b808360ff168151811061285a5761285a613692565b60200260200101519150509392505050565b5f6128756121a8565b54600160401b900460ff16919050565b5f8160ff165f0361289757505f919050565b8160ff166001036128c957507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036128fb57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361292d57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361295f57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361299157507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036129c357507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036129f557507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612a2757507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612a5957507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612a8b57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612abd57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612aef57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612b2157507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612b5357507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612b8557507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612bb757507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612be957507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612c1b57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612c4d57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612c7f57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612cb157507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612ce357507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612d1557507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612d4757507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612d7957507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612dab57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612ddd57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612e0f57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612e4157507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612e7357507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612ea557507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612ed757507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106e9565b602060ff83161115612f435760405162461bcd60e51b81526004016106e990613c30565b5f8364ffffffffff1611612fa75760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106e9565b5f612fb3600185613be1565b9050600181165f0361300657846001015f612fce5f8461272b565b64ffffffffff1681526020019081526020015f2054825f81518110612ff557612ff5613692565b60200260200101818152505061302e565b61300f5f612885565b825f8151811061302157613021613692565b6020026020010181815250505b5f5b8360ff168160ff161015611ec757600182165f036131265773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061308257613082613692565b6020026020010151815260200161309885612885565b8152506040518263ffffffff1660e01b81526004016130b79190613bb1565b602060405180830381865af41580156130d2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130f691906136d2565b83613102836001613b7b565b60ff168151811061311557613115613692565b6020026020010181815250506132d7565b5f613132826001613b7b565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156131d4575f876001015f6131898560016131789190613b7b565b60018864ffffffffff16901c61272b565b64ffffffffff1681526020019081526020015f2054905080858460016131af9190613b7b565b60ff16815181106131c2576131c2613692565b602002602001018181525050506132d5565b5f876001015f6131eb85600188611d529190613be1565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061324257613242613692565b60200260200101518152506040518263ffffffff1660e01b81526004016132699190613bb1565b602060405180830381865af4158015613284573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132a891906136d2565b856132b4856001613b7b565b60ff16815181106132c7576132c7613692565b602002602001018181525050505b505b647fffffffff600192831c169101613030565b60018301918390821561337b579160200282015f5b8382111561334957833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026132ff565b80156133795782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613349565b505b5061338792915061338b565b5090565b5b80821115613387575f815560010161338c565b6001600160a01b03811681146110a9575f5ffd5b5f602082840312156133c3575f5ffd5b813561262b8161339f565b5f602082840312156133de575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b8281101561341e5781516001600160a01b03168652602095860195909101906001016133f7565b5093949350505050565b5f8151808452602084019350602083015f5b8281101561341e57815186526020958601959091019060010161343a565b604081525f61346a60408301856133e5565b828103602084015261347c8185613428565b95945050505050565b5f5f5f60808486031215613497575f5ffd5b8335925060208401359150608084018510156134b1575f5ffd5b6040840190509250925092565b5f5f604083850312156134cf575f5ffd5b8235915060208301356134e18161339f565b809150509250929050565b5f5f5f606084860312156134fe575f5ffd5b8335925060208401356135108161339f565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f61262b60208301846133e5565b5f5f83601f840112613557575f5ffd5b5081356001600160401b0381111561356d575f5ffd5b602083019150836020828501011115613584575f5ffd5b9250929050565b5f5f5f5f5f5f608087890312156135a0575f5ffd5b8635955060208701356001600160401b038111156135bc575f5ffd5b6135c889828a01613547565b9096509450506040870135925060608701356001600160401b038111156135ed575f5ffd5b6135f989828a01613547565b979a9699509497509295939492505050565b5f5f6040838503121561361c575f5ffd5b82356136278161339f565b946020939093013593505050565b5f5f60408385031215613646575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161367757613677613655565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f600182016136cb576136cb613655565b5060010190565b5f602082840312156136e2575f5ffd5b5051919050565b803563ffffffff81168114611bdc575f5ffd5b5f6020828403121561370c575f5ffd5b61262b826136e9565b80820180821115610cb657610cb6613655565b84815260a0810160208201855f5b60028110156137635763ffffffff61374d836136e9565b1683526020928301929190910190600101613736565b50505060608201939093526080015292915050565b80518015158114611bdc575f5ffd5b5f60208284031215613797575f5ffd5b61262b82613778565b6040516101e081016001600160401b03811182821017156137c3576137c361367e565b60405290565b805160048110611bdc575f5ffd5b5f82601f8301126137e6575f5ffd5b604080519081016001600160401b03811182821017156138085761380861367e565b806040525080604084018581111561381e575f5ffd5b845b81811015613838578051835260209283019201613820565b509195945050505050565b8051611bdc8161339f565b805160ff81168114611bdc575f5ffd5b5f82601f83011261386d575f5ffd5b81516001600160401b038111156138865761388661367e565b604051601f8201601f19908116603f011681016001600160401b03811182821017156138b4576138b461367e565b6040528181528382016020018510156138cb575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f602082840312156138f7575f5ffd5b81516001600160401b0381111561390c575f5ffd5b8201610200818503121561391e575f5ffd5b6139266137a0565b81518152613936602083016137c9565b60208201526040828101519082015261395285606084016137d7565b606082015260a0820151608082015261396d60c08301613843565b60a082015261397e60e0830161384e565b60c08201526101008201516001600160401b0381111561399c575f5ffd5b6139a88682850161385e565b60e0830152506139bb6101208301613843565b6101008201526139ce6101408301613843565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a04575f5ffd5b613a108682850161385e565b61018083015250613a246101c08301613843565b6101a0820152613a376101e08301613778565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b848152836020820152606060408201525f613a8c606083018486613a45565b9695505050505050565b5f8154808452602084019350825f5260205f205f5b8281101561341e5781546001600160a01b0316865260209095019460019182019101613aab565b608081525f613ae46080830189613a96565b8281036020840152613af781888a613a45565b90508560408401528281036060840152613b12818587613a45565b9998505050505050505050565b604081525f61346a6040830185613a96565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610cb657610cb6613655565b64ffffffffff8181168382160190811115610cb657610cb6613655565b6040810181835f5b6002811015613bd8578151835260209283019290910190600101613bb9565b50505092915050565b64ffffffffff8281168282160390811115610cb657610cb6613655565b81810381811115610cb657610cb6613655565b5f82613c2b57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610f6657808504811115613c9257613c92613655565b6001841615613ca057908102905b60019390931c928002613c77565b5f82613cbc57506001610cb6565b81613cc857505f610cb6565b8160018114613cde5760028114613ce857613d1a565b6001915050610cb6565b60ff841115613cf957613cf9613655565b6001841b915064ffffffffff821115613d1457613d14613655565b50610cb6565b5060208310610133831016604e8410600b8410161715613d52575081810a64ffffffffff811115613d4d57613d4d613655565b610cb6565b613d6264ffffffffff8484613c73565b8064ffffffffff04821115613d7957613d79613655565b029392505050565b5f61262b64ffffffffff841664ffffffffff8416613cae565b64ffffffffff8181168382160290811690818114613dba57613dba613655565b5092915050565b81515f90829060208501835b828110156138385781516001600160a01b0316845260209384019390910190600101613dcd56fea164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b614548806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061026b575f3560e01c80639a7a2ffc1161014b578063dbb06c93116100bf578063ebf0c71711610084578063ebf0c717146105f3578063f1650536146105fb578063f2fde38b14610615578063f379b0df14610628578063f52fd80314610662578063f6fc05d5146106d2575f5ffd5b8063dbb06c9314610595578063e4be6e3d146105a7578063e59e4695146105ba578063e6745e13146105cd578063e82f3b70146105e0575f5ffd5b8063bff232c111610110578063bff232c11461050d578063c2b40ae414610520578063c3a0ec301461053f578063ca2869a014610550578063cd6dc6871461056f578063da881e5a14610582575f5ffd5b80639a7a2ffc146104735780639f0f874a146104af578063a0164930146104b8578063a8a4d69b146104d8578063b8ab4704146104eb575f5ffd5b80635d504776116101e25780638a78bb15116101a75780638a78bb15146103fd5780638cb89ecb146104105780638d1ddfb11461042f5780638da5cb5b146104455780638e5ce3ad1461044d5780639015d37114610460575f5ffd5b80635d5047761461038257806370e36bbe14610395578063715018a6146103a85780637c92f524146103b057806385814243146103dd575f5ffd5b80632800d829116102335780632800d829146102f1578063291a691b146103045780632e7b716d146103275780634d6861a61461033a57806350e6d94c1461034d5780635302670f1461036f575f5ffd5b8063096b810a1461026f578063099a161a146102845780630bbfade7146102aa5780630f3e3412146102bd57806317d61120146102d0575b5f5ffd5b61028261027d3660046138e0565b6106db565b005b6102976102923660046138fb565b610827565b6040519081526020015b60405180910390f35b6102826102b8366004613956565b610860565b6102826102cb3660046138fb565b610aa4565b6102e36102de3660046138fb565b610ae7565b6040516102a1929190613a77565b6102976102ff3660046138fb565b610c90565b610317610312366004613aa4565b610cdc565b60405190151581526020016102a1565b6103176103353660046138e0565b610eb6565b6103176103483660046138fb565b610f69565b61031761035b3660046138e0565b60066020525f908152604090205460ff1681565b61028261037d3660046138e0565b610fa8565b610317610390366004613add565b610ff9565b6102826103a33660046138e0565b61103c565b6102826110b2565b6103c36103be366004613b0b565b6110c5565b6040805192835263ffffffff9091166020830152016102a1565b6001546103f0906001600160a01b031681565b6040516102a19190613b40565b61028261040b3660046138e0565b61126c565b61029761041e3660046138fb565b60096020525f908152604090205481565b600454600160281b900464ffffffffff16610297565b6103f06113aa565b600b546103f0906001600160a01b031681565b61031761046e3660046138e0565b6113d8565b6104996104813660046138e0565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102a1565b61029760035481565b6104cb6104c63660046138fb565b6113f5565b6040516102a19190613b54565b6103176104e6366004613add565b61148b565b6104fe6104f93660046138fb565b6114ce565b6040516102a193929190613b66565b61028261051b3660046138e0565b611619565b61029761052e3660046138fb565b60086020525f908152604090205481565b6001546001600160a01b03166103f0565b61029761055e3660046138fb565b5f9081526008602052604090205490565b61028261057d366004613ba8565b611691565b6103176105903660046138fb565b6117ee565b5f546103f0906001600160a01b031681565b600c546103f0906001600160a01b031681565b6102826105c83660046138e0565b611ad1565b6102826105db366004613bd2565b611b49565b6102976105ee3660046138fb565b611d0c565b610297611d3d565b610603601481565b60405160ff90911681526020016102a1565b6102826106233660046138e0565b611d4f565b6004546106449064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102a1565b6106a36106703660046138fb565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102a1949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61029760025481565b6106e36113aa565b6001600160a01b0316336001600160a01b0316148061070c57506001546001600160a01b031633145b61072957604051632864c4e160e01b815260040160405180910390fd5b610732816113d8565b819061075b576040516381e5828960e01b81526004016107529190613b40565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107899060049083611d89565b6001600160a01b0382165f908152600660205260408120805460ff1916905560028054916107b683613c06565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a602052604081206004810154610856576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b5f888152600a602052604090206002815460ff16600381111561088557610885613c1b565b146108a357604051634f4b461f60e11b815260040160405180910390fd5b6004810154156108c65760405163632a22bb60e01b815260040160405180910390fd5b856108e457604051636caad1ed60e11b815260040160405180910390fd5b5f6109488260060180548060200260200160405190810160405280929190818152602001828054801561093e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610920575b505050505061202b565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa15801561099a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109c19190810190613dab565b9050806101c00151156109de576109de8b828a858b8b8b8b612131565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610a3c575f5ffd5b505af1158015610a4e573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610a8f96959493929190613f6d565b60405180910390a25050505050505050505050565b610aac61232d565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610b1d57610b1d613c2f565b604051908082528060200260200182016040528015610b46578160200160208202803683370190505b509450806001600160401b03811115610b6157610b61613c2f565b604051908082528060200260200182016040528015610b8a578160200160208202803683370190505b5093505f805b83811015610c86575f856006018281548110610bae57610bae613fba565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610bf457610bf4613c1b565b03610c7d5780888481518110610c0c57610c0c613fba565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610c6457610c64613fba565b602090810291909101015282610c7981613fce565b9350505b50600101610b90565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610cb457610cb4613c1b565b03610cd257604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d075760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d2b57610d2b613c1b565b14610d49576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610d90573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db49190613fe6565b905080610dc76040860160208701614010565b63ffffffff161115610ddf6040860160208701614010565b829091610e0d576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610752565b5050815460ff1916600190811783558201859055436002830155600354610e349042614029565b6003830155610e48600583018560026137de565b50610e51611d3d565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ea2928a928a929161403c565b60405180910390a250600195945050505050565b5f610ec0826113d8565b610ecb57505f919050565b6001546001600160a01b0316610ef4576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f24908590600401613b40565b602060405180830381865afa158015610f3f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f63919061408c565b92915050565b5f818152600a602052604081206001815460ff166003811115610f8e57610f8e613c1b565b14610f9b57505f92915050565b6003015442111592915050565b610fb061232d565b6001600160a01b038116610fd75760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561103457611034613c1b565b149392505050565b61104461232d565b6001600160a01b03811661106b5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6110ba61232d565b6110c35f61235f565b565b600b545f9081906001600160a01b031633146110f45760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561111957611119613c1b565b1461113757604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561117757611177613c1b565b1461118757600b01549150611264565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916111bb83613c06565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161120c929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6112746113aa565b6001600160a01b0316336001600160a01b0316148061129d57506001546001600160a01b031633145b6112ba57604051632864c4e160e01b815260040160405180910390fd5b6112c3816113d8565b6113a75760048054600160281b900464ffffffffff16906112ed906001600160a01b0384166123cf565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161133e83613fce565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161081b565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611428576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561147e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611460575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156114c5576114c5613c1b565b14159392505050565b5f8181526009602052604090205460609081908190611500576040516322e679e360e11b815260040160405180910390fd5b5f848152600d60209081526040808320600e8352818420600f8452938290208154835181860281018601909452808452919493909291859183018282801561156557602002820191905f5260205f20905b815481526020019060010190808311611551575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156115b557602002820191905f5260205f20905b8154815260200190600101908083116115a1575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561160557602002820191905f5260205f20905b8154815260200190600101908083116115f1575b505050505090509250925092509193909250565b61162161232d565b6001600160a01b0381166116485760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f61169a6125a5565b805490915060ff600160401b82041615906001600160401b03165f811580156116c05750825b90505f826001600160401b031660011480156116db5750303b155b9050811580156116e9575080155b156117075760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561173157845460ff60401b1916600160401b1785555b6001600160a01b0387166117585760405163d92e233d60e01b815260040160405180910390fd5b611761336125cd565b61176d600460146125de565b61177686610aa4565b61177e6113aa565b6001600160a01b0316876001600160a01b03161461179f5761179f87611d4f565b83156117e557845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561181257611812613c1b565b0361183057604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561184857611848613c1b565b1461186657604051631860f69960e31b815260040160405180910390fd5b8060030154421161188a57604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061196f578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611950575f5ffd5b505af1158015611962573d5f5f3e3d5ffd5b505f979650505050505050565b6119788261265d565b815460ff191660021782556006820154600b83018190555f816001600160401b038111156119a8576119a8613c2f565b6040519080825280602002602001820160405280156119d1578160200160208202803683370190505b5090505f5b82811015611a4357846009015f8660060183815481106119f8576119f8613fba565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611a3057611a30613fba565b60209081029190910101526001016119d6565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611a86575f5ffd5b505af1158015611a98573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ea29291906140a5565b611ad961232d565b6001600160a01b038116611b005760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611b6d57611b6d613c1b565b03611b8b57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611ba357611ba3613c1b565b14611bc157604051631860f69960e31b815260040160405180910390fd5b8060030154421115611be657604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611c185760405163257309f160e11b815260040160405180910390fd5b611c2133610eb6565b611c3e5760405163149fbcfd60e11b815260040160405180910390fd5b611c49338385612783565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611cc890839083612954565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611d38576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611d4a60046014612b55565b905090565b611d5761232d565b6001600160a01b038116611d80575f604051631e4fbdf760e01b81526004016107529190613b40565b6113a78161235f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611dc85760405162461bcd60e51b8152600401610752906140b7565b825464ffffffffff600160281b90910481169082168111611e265760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610752565b825f5b81866001015f611e398488612c4e565b64ffffffffff1681526020019081526020015f20819055505f816001611e5f9190614101565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611e945750612023565b600185165f03611f5b575f611eb383611eae88600161411a565b612c4e565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f1491600401614137565b602060405180830381865af4158015611f2f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f539190613fe6565b93505061200f565b5f611f6b83611eae600189614167565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611fcc91600401614137565b602060405180830381865af4158015611fe7573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061200b9190613fe6565b9350505b50647fffffffff600194851c169301611e29565b505050505050565b80515f908161203b826014614184565b6001600160401b0381111561205257612052613c2f565b6040519080825280601f01601f19166020018201604052801561207c576020820181803683370190505b5090505f5b82811015612121575f85828151811061209c5761209c613fba565b602002602001015160601b90505f8260146120b79190614184565b90505f5b6014811015612113578281601481106120d6576120d6613fba565b1a60f81b856120e58385614029565b815181106120f5576120f5613fba565b60200101906001600160f81b03191690815f1a9053506001016120bb565b505050806001019050612081565b5080516020909101209392505050565b8261214f57604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b8152600401612186949392919061419b565b602060405180830381865afa1580156121a1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121c5919061408c565b6121e25760405163051d8aa760e51b815260040160405180910390fd5b8061220057604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661222957604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b815260040161227897969594939291906141ba565b5f60405180830381865afa158015612292573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526122b99190810190614294565b5f8e8152600d6020908152604090912084519497509295509093506122e1929086019061387f565b505f8b8152600e6020908152604090912083516123009285019061387f565b505f8b8152600f60209081526040909120825161231f9284019061387f565b505050505050505050505050565b336123366113aa565b6001600160a01b0316146110c3573360405163118cdaa760e01b81526004016107529190613b40565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061241e5760405162461bcd60e51b8152600401610752906140b7565b825464ffffffffff908116908216106124715760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610752565b61247c81600161411a565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6124b38487612c4e565b64ffffffffff16815260208101919091526040015f2055600183161561259e575f6124e382611eae600187614167565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161254491600401614137565b602060405180830381865af415801561255f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125839190613fe6565b647fffffffff600195861c16949093509190910190506124a3565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f63565b6125d5612c6b565b6113a781612c90565b602060ff8216111561262c5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610752565b61263d600160ff831681901b614378565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b8181101561277e575f612678826001614029565b90505b82811015612775575f84600601838154811061269957612699613fba565b5f9182526020822001546006870180546001600160a01b03909216935090849081106126c7576126c7613fba565b5f918252602090912001546001600160a01b039081169150821681101561276b57808660060185815481106126fe576126fe613fba565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055508186600601848154811061273f5761273f613fba565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b505060010161267b565b50600101612664565b505050565b5f82116127a35760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166127cc576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161280291614378565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612849573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061286d9190613fe6565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128c0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128e49190613fe6565b90505f81116129065760405163aeaddff160e01b815260040160405180910390fd5b5f612911828461438b565b90505f81116129335760405163149fbcfd60e11b815260040160405180910390fd5b808611156117e55760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156129d257508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612b4e565b5f5f90505f876009015f855f815481106129ee576129ee613fba565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612a76575f896009015f878481548110612a3857612a38613fba565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612a6d578092508193505b50600101612a17565b50808610612a8a575f945050505050612b4e565b5f88600a015f868581548110612aa257612aa2613fba565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612adf57612adf613c1b565b021790555086848381548110612af757612af7613fba565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612ba85760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610752565b602060ff83161115612bcc5760405162461bcd60e51b8152600401610752906143aa565b8254600160281b900464ffffffffff1680612beb60ff851660026144fb565b64ffffffffff161015612c3b5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610752565b612c46848285612c98565b949350505050565b5f81612c6160ff851663ffffffff614514565b612b4e919061411a565b612c73612d60565b6110c357604051631afcd79f60e31b815260040160405180910390fd5b611d57612c6b565b5f602060ff83161115612cbd5760405162461bcd60e51b8152600401610752906143aa565b8264ffffffffff165f03612cdb57612cd482612d79565b9050612b4e565b5f612ce7836001614101565b60ff166001600160401b03811115612d0157612d01613c2f565b604051908082528060200260200182016040528015612d2a578160200160208202803683370190505b509050612d3985858584613413565b808360ff1681518110612d4e57612d4e613fba565b60200260200101519150509392505050565b5f612d696125a5565b54600160401b900460ff16919050565b5f8160ff165f03612d8b57505f919050565b8160ff16600103612dbd57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff16600203612def57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff16600303612e2157507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff16600403612e5357507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff16600503612e8557507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff16600603612eb757507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff16600703612ee957507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612f1b57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612f4d57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612f7f57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612fb157507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612fe357507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361301557507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361304757507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361307957507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036130ab57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff166011036130dd57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361310f57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361314157507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361317357507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036131a557507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff166016036131d757507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361320957507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361323b57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361326d57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361329f57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b036132d157507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361330357507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361333557507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361336757507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361339957507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036133cb57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610752565b602060ff831611156134375760405162461bcd60e51b8152600401610752906143aa565b5f8364ffffffffff161161349b5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610752565b5f6134a7600185614167565b9050600181165f036134fa57846001015f6134c25f84612c4e565b64ffffffffff1681526020019081526020015f2054825f815181106134e9576134e9613fba565b602002602001018181525050613522565b6135035f612d79565b825f8151811061351557613515613fba565b6020026020010181815250505b5f5b8360ff168160ff16101561202357600182165f0361361a5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061357657613576613fba565b6020026020010151815260200161358c85612d79565b8152506040518263ffffffff1660e01b81526004016135ab9190614137565b602060405180830381865af41580156135c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135ea9190613fe6565b836135f6836001614101565b60ff168151811061360957613609613fba565b6020026020010181815250506137cb565b5f613626826001614101565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156136c8575f876001015f61367d85600161366c9190614101565b60018864ffffffffff16901c612c4e565b64ffffffffff1681526020019081526020015f2054905080858460016136a39190614101565b60ff16815181106136b6576136b6613fba565b602002602001018181525050506137c9565b5f876001015f6136df85600188611eae9190614167565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061373657613736613fba565b60200260200101518152506040518263ffffffff1660e01b815260040161375d9190614137565b602060405180830381865af4158015613778573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061379c9190613fe6565b856137a8856001614101565b60ff16815181106137bb576137bb613fba565b602002602001018181525050505b505b647fffffffff600192831c169101613524565b60018301918390821561386f579160200282015f5b8382111561383d57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026137f3565b801561386d5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261383d565b505b5061387b9291506138b8565b5090565b828054828255905f5260205f2090810192821561386f579160200282015b8281111561386f57825182559160200191906001019061389d565b5b8082111561387b575f81556001016138b9565b6001600160a01b03811681146113a7575f5ffd5b5f602082840312156138f0575f5ffd5b8135612b4e816138cc565b5f6020828403121561390b575f5ffd5b5035919050565b5f5f83601f840112613922575f5ffd5b5081356001600160401b03811115613938575f5ffd5b60208301915083602082850101111561394f575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b03121561396d575f5ffd5b8835975060208901356001600160401b03811115613989575f5ffd5b6139958b828c01613912565b9098509650506040890135945060608901356001600160401b038111156139ba575f5ffd5b6139c68b828c01613912565b90955093505060808901356001600160401b038111156139e4575f5ffd5b6139f08b828c01613912565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613a3d5781516001600160a01b0316865260209586019590910190600101613a16565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613a3d578151865260209586019590910190600101613a59565b604081525f613a896040830185613a04565b8281036020840152613a9b8185613a47565b95945050505050565b5f5f5f60808486031215613ab6575f5ffd5b833592506020840135915060808401851015613ad0575f5ffd5b6040840190509250925092565b5f5f60408385031215613aee575f5ffd5b823591506020830135613b00816138cc565b809150509250929050565b5f5f5f60608486031215613b1d575f5ffd5b833592506020840135613b2f816138cc565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612b4e6020830184613a04565b606081525f613b786060830186613a47565b8281036020840152613b8a8186613a47565b90508281036040840152613b9e8185613a47565b9695505050505050565b5f5f60408385031215613bb9575f5ffd5b8235613bc4816138cc565b946020939093013593505050565b5f5f60408385031215613be3575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613c1457613c14613bf2565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613c6657613c66613c2f565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613c9457613c94613c2f565b604052919050565b805160048110611d38575f5ffd5b5f82601f830112613cb9575f5ffd5b604080519081016001600160401b0381118282101715613cdb57613cdb613c2f565b8060405250806040840185811115613cf1575f5ffd5b845b81811015613d0b578051835260209283019201613cf3565b509195945050505050565b8051611d38816138cc565b805160ff81168114611d38575f5ffd5b5f82601f830112613d40575f5ffd5b81516001600160401b03811115613d5957613d59613c2f565b613d6c601f8201601f1916602001613c6c565b818152846020838601011115613d80575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611d38575f5ffd5b5f60208284031215613dbb575f5ffd5b81516001600160401b03811115613dd0575f5ffd5b82016102008185031215613de2575f5ffd5b613dea613c43565b81518152613dfa60208301613c9c565b602082015260408281015190820152613e168560608401613caa565b606082015260a08201516080820152613e3160c08301613d16565b60a0820152613e4260e08301613d21565b60c08201526101008201516001600160401b03811115613e60575f5ffd5b613e6c86828501613d31565b60e083015250613e7f6101208301613d16565b610100820152613e926101408301613d16565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613ec8575f5ffd5b613ed486828501613d31565b61018083015250613ee86101c08301613d16565b6101a0820152613efb6101e08301613d9c565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613a3d5781546001600160a01b0316865260209095019460019182019101613f1e565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f613f7f6080830189613f09565b8281036020840152613f9281888a613f45565b90508560408401528281036060840152613fad818587613f45565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60018201613fdf57613fdf613bf2565b5060010190565b5f60208284031215613ff6575f5ffd5b5051919050565b803563ffffffff81168114611d38575f5ffd5b5f60208284031215614020575f5ffd5b612b4e82613ffd565b80820180821115610f6357610f63613bf2565b84815260a0810160208201855f5b60028110156140775763ffffffff61406183613ffd565b168352602092830192919091019060010161404a565b50505060608201939093526080015292915050565b5f6020828403121561409c575f5ffd5b612b4e82613d9c565b604081525f613a896040830185613f09565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f6357610f63613bf2565b64ffffffffff8181168382160190811115610f6357610f63613bf2565b6040810181835f5b600281101561415e57815183526020928301929091019060010161413f565b50505092915050565b64ffffffffff8281168282160390811115610f6357610f63613bf2565b8082028115828204841417610f6357610f63613bf2565b848152836020820152606060408201525f613b9e606083018486613f45565b60018060a01b038816815286602082015285604082015260a060608201525f6141e760a083018688613f45565b82810360808401526141fa818587613f45565b9a9950505050505050505050565b5f6001600160401b0382111561422057614220613c2f565b5060051b60200190565b5f82601f830112614239575f5ffd5b815161424c61424782614208565b613c6c565b8082825260208201915060208360051b86010192508583111561426d575f5ffd5b602085015b8381101561428a578051835260209283019201614272565b5095945050505050565b5f5f5f606084860312156142a6575f5ffd5b83516001600160401b038111156142bb575f5ffd5b8401601f810186136142cb575f5ffd5b80516142d961424782614208565b8082825260208201915060208360051b8501019250888311156142fa575f5ffd5b6020840193505b8284101561431c578351825260209384019390910190614301565b8096505050505060208401516001600160401b0381111561433b575f5ffd5b6143478682870161422a565b92505060408401516001600160401b03811115614362575f5ffd5b61436e8682870161422a565b9150509250925092565b81810381811115610f6357610f63613bf2565b5f826143a557634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156112645780850481111561440c5761440c613bf2565b600184161561441a57908102905b60019390931c9280026143f1565b5f8261443657506001610f63565b8161444257505f610f63565b8160018114614458576002811461446257614494565b6001915050610f63565b60ff84111561447357614473613bf2565b6001841b915064ffffffffff82111561448e5761448e613bf2565b50610f63565b5060208310610133831016604e8410600b84101617156144cc575081810a64ffffffffff8111156144c7576144c7613bf2565b610f63565b6144dc64ffffffffff84846143ed565b8064ffffffffff048211156144f3576144f3613bf2565b029392505050565b5f612b4e64ffffffffff841664ffffffffff8416614428565b64ffffffffff818116838216029081169081811461453457614534613bf2565b509291505056fea164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061026b575f3560e01c80639a7a2ffc1161014b578063dbb06c93116100bf578063ebf0c71711610084578063ebf0c717146105f3578063f1650536146105fb578063f2fde38b14610615578063f379b0df14610628578063f52fd80314610662578063f6fc05d5146106d2575f5ffd5b8063dbb06c9314610595578063e4be6e3d146105a7578063e59e4695146105ba578063e6745e13146105cd578063e82f3b70146105e0575f5ffd5b8063bff232c111610110578063bff232c11461050d578063c2b40ae414610520578063c3a0ec301461053f578063ca2869a014610550578063cd6dc6871461056f578063da881e5a14610582575f5ffd5b80639a7a2ffc146104735780639f0f874a146104af578063a0164930146104b8578063a8a4d69b146104d8578063b8ab4704146104eb575f5ffd5b80635d504776116101e25780638a78bb15116101a75780638a78bb15146103fd5780638cb89ecb146104105780638d1ddfb11461042f5780638da5cb5b146104455780638e5ce3ad1461044d5780639015d37114610460575f5ffd5b80635d5047761461038257806370e36bbe14610395578063715018a6146103a85780637c92f524146103b057806385814243146103dd575f5ffd5b80632800d829116102335780632800d829146102f1578063291a691b146103045780632e7b716d146103275780634d6861a61461033a57806350e6d94c1461034d5780635302670f1461036f575f5ffd5b8063096b810a1461026f578063099a161a146102845780630bbfade7146102aa5780630f3e3412146102bd57806317d61120146102d0575b5f5ffd5b61028261027d3660046138e0565b6106db565b005b6102976102923660046138fb565b610827565b6040519081526020015b60405180910390f35b6102826102b8366004613956565b610860565b6102826102cb3660046138fb565b610aa4565b6102e36102de3660046138fb565b610ae7565b6040516102a1929190613a77565b6102976102ff3660046138fb565b610c90565b610317610312366004613aa4565b610cdc565b60405190151581526020016102a1565b6103176103353660046138e0565b610eb6565b6103176103483660046138fb565b610f69565b61031761035b3660046138e0565b60066020525f908152604090205460ff1681565b61028261037d3660046138e0565b610fa8565b610317610390366004613add565b610ff9565b6102826103a33660046138e0565b61103c565b6102826110b2565b6103c36103be366004613b0b565b6110c5565b6040805192835263ffffffff9091166020830152016102a1565b6001546103f0906001600160a01b031681565b6040516102a19190613b40565b61028261040b3660046138e0565b61126c565b61029761041e3660046138fb565b60096020525f908152604090205481565b600454600160281b900464ffffffffff16610297565b6103f06113aa565b600b546103f0906001600160a01b031681565b61031761046e3660046138e0565b6113d8565b6104996104813660046138e0565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102a1565b61029760035481565b6104cb6104c63660046138fb565b6113f5565b6040516102a19190613b54565b6103176104e6366004613add565b61148b565b6104fe6104f93660046138fb565b6114ce565b6040516102a193929190613b66565b61028261051b3660046138e0565b611619565b61029761052e3660046138fb565b60086020525f908152604090205481565b6001546001600160a01b03166103f0565b61029761055e3660046138fb565b5f9081526008602052604090205490565b61028261057d366004613ba8565b611691565b6103176105903660046138fb565b6117ee565b5f546103f0906001600160a01b031681565b600c546103f0906001600160a01b031681565b6102826105c83660046138e0565b611ad1565b6102826105db366004613bd2565b611b49565b6102976105ee3660046138fb565b611d0c565b610297611d3d565b610603601481565b60405160ff90911681526020016102a1565b6102826106233660046138e0565b611d4f565b6004546106449064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102a1565b6106a36106703660046138fb565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102a1949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61029760025481565b6106e36113aa565b6001600160a01b0316336001600160a01b0316148061070c57506001546001600160a01b031633145b61072957604051632864c4e160e01b815260040160405180910390fd5b610732816113d8565b819061075b576040516381e5828960e01b81526004016107529190613b40565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107899060049083611d89565b6001600160a01b0382165f908152600660205260408120805460ff1916905560028054916107b683613c06565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a602052604081206004810154610856576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b5f888152600a602052604090206002815460ff16600381111561088557610885613c1b565b146108a357604051634f4b461f60e11b815260040160405180910390fd5b6004810154156108c65760405163632a22bb60e01b815260040160405180910390fd5b856108e457604051636caad1ed60e11b815260040160405180910390fd5b5f6109488260060180548060200260200160405190810160405280929190818152602001828054801561093e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610920575b505050505061202b565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa15801561099a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109c19190810190613dab565b9050806101c00151156109de576109de8b828a858b8b8b8b612131565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610a3c575f5ffd5b505af1158015610a4e573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610a8f96959493929190613f6d565b60405180910390a25050505050505050505050565b610aac61232d565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610b1d57610b1d613c2f565b604051908082528060200260200182016040528015610b46578160200160208202803683370190505b509450806001600160401b03811115610b6157610b61613c2f565b604051908082528060200260200182016040528015610b8a578160200160208202803683370190505b5093505f805b83811015610c86575f856006018281548110610bae57610bae613fba565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610bf457610bf4613c1b565b03610c7d5780888481518110610c0c57610c0c613fba565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610c6457610c64613fba565b602090810291909101015282610c7981613fce565b9350505b50600101610b90565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610cb457610cb4613c1b565b03610cd257604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d075760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d2b57610d2b613c1b565b14610d49576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610d90573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db49190613fe6565b905080610dc76040860160208701614010565b63ffffffff161115610ddf6040860160208701614010565b829091610e0d576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610752565b5050815460ff1916600190811783558201859055436002830155600354610e349042614029565b6003830155610e48600583018560026137de565b50610e51611d3d565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ea2928a928a929161403c565b60405180910390a250600195945050505050565b5f610ec0826113d8565b610ecb57505f919050565b6001546001600160a01b0316610ef4576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f24908590600401613b40565b602060405180830381865afa158015610f3f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f63919061408c565b92915050565b5f818152600a602052604081206001815460ff166003811115610f8e57610f8e613c1b565b14610f9b57505f92915050565b6003015442111592915050565b610fb061232d565b6001600160a01b038116610fd75760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561103457611034613c1b565b149392505050565b61104461232d565b6001600160a01b03811661106b5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6110ba61232d565b6110c35f61235f565b565b600b545f9081906001600160a01b031633146110f45760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561111957611119613c1b565b1461113757604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561117757611177613c1b565b1461118757600b01549150611264565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916111bb83613c06565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161120c929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6112746113aa565b6001600160a01b0316336001600160a01b0316148061129d57506001546001600160a01b031633145b6112ba57604051632864c4e160e01b815260040160405180910390fd5b6112c3816113d8565b6113a75760048054600160281b900464ffffffffff16906112ed906001600160a01b0384166123cf565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161133e83613fce565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161081b565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611428576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561147e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611460575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156114c5576114c5613c1b565b14159392505050565b5f8181526009602052604090205460609081908190611500576040516322e679e360e11b815260040160405180910390fd5b5f848152600d60209081526040808320600e8352818420600f8452938290208154835181860281018601909452808452919493909291859183018282801561156557602002820191905f5260205f20905b815481526020019060010190808311611551575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156115b557602002820191905f5260205f20905b8154815260200190600101908083116115a1575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561160557602002820191905f5260205f20905b8154815260200190600101908083116115f1575b505050505090509250925092509193909250565b61162161232d565b6001600160a01b0381166116485760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f61169a6125a5565b805490915060ff600160401b82041615906001600160401b03165f811580156116c05750825b90505f826001600160401b031660011480156116db5750303b155b9050811580156116e9575080155b156117075760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561173157845460ff60401b1916600160401b1785555b6001600160a01b0387166117585760405163d92e233d60e01b815260040160405180910390fd5b611761336125cd565b61176d600460146125de565b61177686610aa4565b61177e6113aa565b6001600160a01b0316876001600160a01b03161461179f5761179f87611d4f565b83156117e557845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561181257611812613c1b565b0361183057604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561184857611848613c1b565b1461186657604051631860f69960e31b815260040160405180910390fd5b8060030154421161188a57604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061196f578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611950575f5ffd5b505af1158015611962573d5f5f3e3d5ffd5b505f979650505050505050565b6119788261265d565b815460ff191660021782556006820154600b83018190555f816001600160401b038111156119a8576119a8613c2f565b6040519080825280602002602001820160405280156119d1578160200160208202803683370190505b5090505f5b82811015611a4357846009015f8660060183815481106119f8576119f8613fba565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611a3057611a30613fba565b60209081029190910101526001016119d6565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611a86575f5ffd5b505af1158015611a98573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ea29291906140a5565b611ad961232d565b6001600160a01b038116611b005760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611b6d57611b6d613c1b565b03611b8b57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611ba357611ba3613c1b565b14611bc157604051631860f69960e31b815260040160405180910390fd5b8060030154421115611be657604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611c185760405163257309f160e11b815260040160405180910390fd5b611c2133610eb6565b611c3e5760405163149fbcfd60e11b815260040160405180910390fd5b611c49338385612783565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611cc890839083612954565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611d38576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611d4a60046014612b55565b905090565b611d5761232d565b6001600160a01b038116611d80575f604051631e4fbdf760e01b81526004016107529190613b40565b6113a78161235f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611dc85760405162461bcd60e51b8152600401610752906140b7565b825464ffffffffff600160281b90910481169082168111611e265760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610752565b825f5b81866001015f611e398488612c4e565b64ffffffffff1681526020019081526020015f20819055505f816001611e5f9190614101565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611e945750612023565b600185165f03611f5b575f611eb383611eae88600161411a565b612c4e565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f1491600401614137565b602060405180830381865af4158015611f2f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f539190613fe6565b93505061200f565b5f611f6b83611eae600189614167565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611fcc91600401614137565b602060405180830381865af4158015611fe7573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061200b9190613fe6565b9350505b50647fffffffff600194851c169301611e29565b505050505050565b80515f908161203b826014614184565b6001600160401b0381111561205257612052613c2f565b6040519080825280601f01601f19166020018201604052801561207c576020820181803683370190505b5090505f5b82811015612121575f85828151811061209c5761209c613fba565b602002602001015160601b90505f8260146120b79190614184565b90505f5b6014811015612113578281601481106120d6576120d6613fba565b1a60f81b856120e58385614029565b815181106120f5576120f5613fba565b60200101906001600160f81b03191690815f1a9053506001016120bb565b505050806001019050612081565b5080516020909101209392505050565b8261214f57604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b8152600401612186949392919061419b565b602060405180830381865afa1580156121a1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121c5919061408c565b6121e25760405163051d8aa760e51b815260040160405180910390fd5b8061220057604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661222957604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b815260040161227897969594939291906141ba565b5f60405180830381865afa158015612292573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526122b99190810190614294565b5f8e8152600d6020908152604090912084519497509295509093506122e1929086019061387f565b505f8b8152600e6020908152604090912083516123009285019061387f565b505f8b8152600f60209081526040909120825161231f9284019061387f565b505050505050505050505050565b336123366113aa565b6001600160a01b0316146110c3573360405163118cdaa760e01b81526004016107529190613b40565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061241e5760405162461bcd60e51b8152600401610752906140b7565b825464ffffffffff908116908216106124715760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610752565b61247c81600161411a565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6124b38487612c4e565b64ffffffffff16815260208101919091526040015f2055600183161561259e575f6124e382611eae600187614167565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161254491600401614137565b602060405180830381865af415801561255f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125839190613fe6565b647fffffffff600195861c16949093509190910190506124a3565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f63565b6125d5612c6b565b6113a781612c90565b602060ff8216111561262c5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610752565b61263d600160ff831681901b614378565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b8181101561277e575f612678826001614029565b90505b82811015612775575f84600601838154811061269957612699613fba565b5f9182526020822001546006870180546001600160a01b03909216935090849081106126c7576126c7613fba565b5f918252602090912001546001600160a01b039081169150821681101561276b57808660060185815481106126fe576126fe613fba565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055508186600601848154811061273f5761273f613fba565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b505060010161267b565b50600101612664565b505050565b5f82116127a35760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166127cc576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161280291614378565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612849573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061286d9190613fe6565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128c0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128e49190613fe6565b90505f81116129065760405163aeaddff160e01b815260040160405180910390fd5b5f612911828461438b565b90505f81116129335760405163149fbcfd60e11b815260040160405180910390fd5b808611156117e55760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156129d257508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612b4e565b5f5f90505f876009015f855f815481106129ee576129ee613fba565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612a76575f896009015f878481548110612a3857612a38613fba565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612a6d578092508193505b50600101612a17565b50808610612a8a575f945050505050612b4e565b5f88600a015f868581548110612aa257612aa2613fba565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612adf57612adf613c1b565b021790555086848381548110612af757612af7613fba565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612ba85760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610752565b602060ff83161115612bcc5760405162461bcd60e51b8152600401610752906143aa565b8254600160281b900464ffffffffff1680612beb60ff851660026144fb565b64ffffffffff161015612c3b5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610752565b612c46848285612c98565b949350505050565b5f81612c6160ff851663ffffffff614514565b612b4e919061411a565b612c73612d60565b6110c357604051631afcd79f60e31b815260040160405180910390fd5b611d57612c6b565b5f602060ff83161115612cbd5760405162461bcd60e51b8152600401610752906143aa565b8264ffffffffff165f03612cdb57612cd482612d79565b9050612b4e565b5f612ce7836001614101565b60ff166001600160401b03811115612d0157612d01613c2f565b604051908082528060200260200182016040528015612d2a578160200160208202803683370190505b509050612d3985858584613413565b808360ff1681518110612d4e57612d4e613fba565b60200260200101519150509392505050565b5f612d696125a5565b54600160401b900460ff16919050565b5f8160ff165f03612d8b57505f919050565b8160ff16600103612dbd57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff16600203612def57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff16600303612e2157507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff16600403612e5357507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff16600503612e8557507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff16600603612eb757507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff16600703612ee957507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612f1b57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612f4d57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612f7f57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612fb157507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612fe357507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361301557507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361304757507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361307957507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036130ab57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff166011036130dd57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361310f57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361314157507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361317357507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036131a557507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff166016036131d757507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361320957507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361323b57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361326d57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361329f57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b036132d157507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361330357507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361333557507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361336757507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361339957507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036133cb57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610752565b602060ff831611156134375760405162461bcd60e51b8152600401610752906143aa565b5f8364ffffffffff161161349b5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610752565b5f6134a7600185614167565b9050600181165f036134fa57846001015f6134c25f84612c4e565b64ffffffffff1681526020019081526020015f2054825f815181106134e9576134e9613fba565b602002602001018181525050613522565b6135035f612d79565b825f8151811061351557613515613fba565b6020026020010181815250505b5f5b8360ff168160ff16101561202357600182165f0361361a5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061357657613576613fba565b6020026020010151815260200161358c85612d79565b8152506040518263ffffffff1660e01b81526004016135ab9190614137565b602060405180830381865af41580156135c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135ea9190613fe6565b836135f6836001614101565b60ff168151811061360957613609613fba565b6020026020010181815250506137cb565b5f613626826001614101565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156136c8575f876001015f61367d85600161366c9190614101565b60018864ffffffffff16901c612c4e565b64ffffffffff1681526020019081526020015f2054905080858460016136a39190614101565b60ff16815181106136b6576136b6613fba565b602002602001018181525050506137c9565b5f876001015f6136df85600188611eae9190614167565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061373657613736613fba565b60200260200101518152506040518263ffffffff1660e01b815260040161375d9190614137565b602060405180830381865af4158015613778573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061379c9190613fe6565b856137a8856001614101565b60ff16815181106137bb576137bb613fba565b602002602001018181525050505b505b647fffffffff600192831c169101613524565b60018301918390821561386f579160200282015f5b8382111561383d57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026137f3565b801561386d5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261383d565b505b5061387b9291506138b8565b5090565b828054828255905f5260205f2090810192821561386f579160200282015b8281111561386f57825182559160200191906001019061389d565b5b8082111561387b575f81556001016138b9565b6001600160a01b03811681146113a7575f5ffd5b5f602082840312156138f0575f5ffd5b8135612b4e816138cc565b5f6020828403121561390b575f5ffd5b5035919050565b5f5f83601f840112613922575f5ffd5b5081356001600160401b03811115613938575f5ffd5b60208301915083602082850101111561394f575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b03121561396d575f5ffd5b8835975060208901356001600160401b03811115613989575f5ffd5b6139958b828c01613912565b9098509650506040890135945060608901356001600160401b038111156139ba575f5ffd5b6139c68b828c01613912565b90955093505060808901356001600160401b038111156139e4575f5ffd5b6139f08b828c01613912565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613a3d5781516001600160a01b0316865260209586019590910190600101613a16565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613a3d578151865260209586019590910190600101613a59565b604081525f613a896040830185613a04565b8281036020840152613a9b8185613a47565b95945050505050565b5f5f5f60808486031215613ab6575f5ffd5b833592506020840135915060808401851015613ad0575f5ffd5b6040840190509250925092565b5f5f60408385031215613aee575f5ffd5b823591506020830135613b00816138cc565b809150509250929050565b5f5f5f60608486031215613b1d575f5ffd5b833592506020840135613b2f816138cc565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612b4e6020830184613a04565b606081525f613b786060830186613a47565b8281036020840152613b8a8186613a47565b90508281036040840152613b9e8185613a47565b9695505050505050565b5f5f60408385031215613bb9575f5ffd5b8235613bc4816138cc565b946020939093013593505050565b5f5f60408385031215613be3575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613c1457613c14613bf2565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613c6657613c66613c2f565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613c9457613c94613c2f565b604052919050565b805160048110611d38575f5ffd5b5f82601f830112613cb9575f5ffd5b604080519081016001600160401b0381118282101715613cdb57613cdb613c2f565b8060405250806040840185811115613cf1575f5ffd5b845b81811015613d0b578051835260209283019201613cf3565b509195945050505050565b8051611d38816138cc565b805160ff81168114611d38575f5ffd5b5f82601f830112613d40575f5ffd5b81516001600160401b03811115613d5957613d59613c2f565b613d6c601f8201601f1916602001613c6c565b818152846020838601011115613d80575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611d38575f5ffd5b5f60208284031215613dbb575f5ffd5b81516001600160401b03811115613dd0575f5ffd5b82016102008185031215613de2575f5ffd5b613dea613c43565b81518152613dfa60208301613c9c565b602082015260408281015190820152613e168560608401613caa565b606082015260a08201516080820152613e3160c08301613d16565b60a0820152613e4260e08301613d21565b60c08201526101008201516001600160401b03811115613e60575f5ffd5b613e6c86828501613d31565b60e083015250613e7f6101208301613d16565b610100820152613e926101408301613d16565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613ec8575f5ffd5b613ed486828501613d31565b61018083015250613ee86101c08301613d16565b6101a0820152613efb6101e08301613d9c565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613a3d5781546001600160a01b0316865260209095019460019182019101613f1e565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f613f7f6080830189613f09565b8281036020840152613f9281888a613f45565b90508560408401528281036060840152613fad818587613f45565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60018201613fdf57613fdf613bf2565b5060010190565b5f60208284031215613ff6575f5ffd5b5051919050565b803563ffffffff81168114611d38575f5ffd5b5f60208284031215614020575f5ffd5b612b4e82613ffd565b80820180821115610f6357610f63613bf2565b84815260a0810160208201855f5b60028110156140775763ffffffff61406183613ffd565b168352602092830192919091019060010161404a565b50505060608201939093526080015292915050565b5f6020828403121561409c575f5ffd5b612b4e82613d9c565b604081525f613a896040830185613f09565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f6357610f63613bf2565b64ffffffffff8181168382160190811115610f6357610f63613bf2565b6040810181835f5b600281101561415e57815183526020928301929091019060010161413f565b50505092915050565b64ffffffffff8281168282160390811115610f6357610f63613bf2565b8082028115828204841417610f6357610f63613bf2565b848152836020820152606060408201525f613b9e606083018486613f45565b60018060a01b038816815286602082015285604082015260a060608201525f6141e760a083018688613f45565b82810360808401526141fa818587613f45565b9a9950505050505050505050565b5f6001600160401b0382111561422057614220613c2f565b5060051b60200190565b5f82601f830112614239575f5ffd5b815161424c61424782614208565b613c6c565b8082825260208201915060208360051b86010192508583111561426d575f5ffd5b602085015b8381101561428a578051835260209283019201614272565b5095945050505050565b5f5f5f606084860312156142a6575f5ffd5b83516001600160401b038111156142bb575f5ffd5b8401601f810186136142cb575f5ffd5b80516142d961424782614208565b8082825260208201915060208360051b8501019250888311156142fa575f5ffd5b6020840193505b8284101561431c578351825260209384019390910190614301565b8096505050505060208401516001600160401b0381111561433b575f5ffd5b6143478682870161422a565b92505060408401516001600160401b03811115614362575f5ffd5b61436e8682870161422a565b9150509250925092565b81810381811115610f6357610f63613bf2565b5f826143a557634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156112645780850481111561440c5761440c613bf2565b600184161561441a57908102905b60019390931c9280026143f1565b5f8261443657506001610f63565b8161444257505f610f63565b8160018114614458576002811461446257614494565b6001915050610f63565b60ff84111561447357614473613bf2565b6001841b915064ffffffffff82111561448e5761448e613bf2565b50610f63565b5060208310610133831016604e8410600b84101617156144cc575081810a64ffffffffff8111156144c7576144c7613bf2565b610f63565b6144dc64ffffffffff84846143ed565b8064ffffffffff048211156144f3576144f3613bf2565b029392505050565b5f612b4e64ffffffffff841664ffffffffff8416614428565b64ffffffffff818116838216029081169081811461453457614534613bf2565b509291505056fea164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 7784 + "start": 8132 }, { "length": 20, - "start": 7968 + "start": 8316 }, { "length": 20, - "start": 8598 + "start": 9716 }, { "length": 20, - "start": 12576 + "start": 13844 }, { "length": 20, - "start": 13018 + "start": 14286 } ] } @@ -1330,28 +1430,28 @@ "PoseidonT3": [ { "length": 20, - "start": 7570 + "start": 7918 }, { "length": 20, - "start": 7754 + "start": 8102 }, { "length": 20, - "start": 8384 + "start": 9502 }, { "length": 20, - "start": 12362 + "start": 13630 }, { "length": 20, - "start": 12804 + "start": 14072 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-23df357f03d1ab6ff0a7509a32ecf84190997859" + "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json index 283b2a4ff..c4acccb04 100644 --- a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json +++ b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json @@ -1193,7 +1193,7 @@ "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": { - "2832": [ + "3415": [ { "length": 32, "start": 2182 @@ -1211,43 +1211,43 @@ "start": 3705 } ], - "5819": [ + "6684": [ { "length": 32, "start": 3936 } ], - "5821": [ + "6686": [ { "length": 32, "start": 3894 } ], - "5823": [ + "6688": [ { "length": 32, "start": 3852 } ], - "5825": [ + "6690": [ { "length": 32, "start": 4017 } ], - "5827": [ + "6692": [ { "length": 32, "start": 4057 } ], - "5830": [ + "6695": [ { "length": 32, "start": 4712 } ], - "5833": [ + "6698": [ { "length": 32, "start": 4757 @@ -1255,5 +1255,5 @@ ] }, "inputSourceName": "project/contracts/token/EnclaveTicketToken.sol", - "buildInfoId": "solc-0_8_28-2705a75bc2d2d1f8b1e08ebca4cc37d76480abc8" + "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" } \ No newline at end of file From 98592e55a28f9985499d8a0e027129625bee00ce Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 18:55:45 +0200 Subject: [PATCH 25/54] update template --- .../contracts/Mocks/MockRISC0Verifier.sol | 6 +-- templates/default/contracts/MyProgram.sol | 10 ++-- templates/default/deploy/default.ts | 24 ++++----- templates/default/deployed_contracts.json | 54 ++++++------------- templates/default/enclave.config.yaml | 12 ++--- templates/default/hardhat.config.ts | 10 ++++ templates/default/package.json | 2 +- templates/default/remappings.txt | 5 ++ templates/default/scripts/anvil-automine.mjs | 32 +++++++++++ templates/default/scripts/deploy-local.ts | 12 +++-- templates/default/scripts/dev_ciphernodes.sh | 27 +++++++--- templates/default/scripts/template-paths.ts | 21 ++++++++ templates/default/scripts/test_integration.sh | 12 +++-- templates/default/tests/anvil-helpers.ts | 19 +++++++ templates/default/tests/integration.spec.ts | 15 +++++- 15 files changed, 178 insertions(+), 83 deletions(-) create mode 100644 templates/default/remappings.txt create mode 100644 templates/default/scripts/anvil-automine.mjs create mode 100644 templates/default/scripts/template-paths.ts create mode 100644 templates/default/tests/anvil-helpers.ts diff --git a/templates/default/contracts/Mocks/MockRISC0Verifier.sol b/templates/default/contracts/Mocks/MockRISC0Verifier.sol index 450573586..566ff3469 100644 --- a/templates/default/contracts/Mocks/MockRISC0Verifier.sol +++ b/templates/default/contracts/Mocks/MockRISC0Verifier.sol @@ -3,12 +3,12 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pragma solidity ^0.8.27; +pragma solidity 0.8.28; -import { IRiscZeroVerifier, Receipt } from "@risc0/ethereum/contracts/IRiscZeroVerifier.sol"; +import { IRiscZeroVerifier, Receipt } from "risc0/IRiscZeroVerifier.sol"; contract MockRISC0Verifier is IRiscZeroVerifier { - function verify(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) public view override {} + function verify(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) external view override {} function verifyIntegrity(Receipt calldata receipt) external view override {} } diff --git a/templates/default/contracts/MyProgram.sol b/templates/default/contracts/MyProgram.sol index f02def5b4..b33ffaa5d 100755 --- a/templates/default/contracts/MyProgram.sol +++ b/templates/default/contracts/MyProgram.sol @@ -3,9 +3,9 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pragma solidity >=0.8.27; +pragma solidity 0.8.28; -import { IRiscZeroVerifier } from "@risc0/ethereum/contracts/IRiscZeroVerifier.sol"; +import { IRiscZeroVerifier } from "risc0/IRiscZeroVerifier.sol"; import { IE3Program } from "@enclave-e3/contracts/contracts/interfaces/IE3Program.sol"; import { IEnclave } from "@enclave-e3/contracts/contracts/interfaces/IEnclave.sol"; import { E3 } from "@enclave-e3/contracts/contracts/interfaces/IE3.sol"; @@ -53,9 +53,7 @@ contract MyProgram is IE3Program, Ownable { authorizedContracts[address(_enclave)] = true; } - /// @notice Validate the E3 program parameters - /// @param e3Id The E3 program ID - /// @param e3ProgramParams The E3 program parameters + /// @inheritdoc IE3Program function validate(uint256 e3Id, uint256, bytes calldata e3ProgramParams, bytes calldata, bytes calldata) external returns (bytes32) { require(authorizedContracts[msg.sender] || msg.sender == owner(), CallerNotAuthorized()); require(paramsHashes[e3Id] == bytes32(0), E3AlreadyInitialized()); @@ -91,7 +89,7 @@ contract MyProgram is IE3Program, Ownable { /// @param e3Id The E3 program ID /// @param ciphertextOutputHash The hash of the ciphertext output /// @param proof The proof to verify - function verify(uint256 e3Id, bytes32 ciphertextOutputHash, bytes memory proof) external view override returns (bool) { + function verify(uint256 e3Id, bytes32 ciphertextOutputHash, bytes memory proof) external override returns (bool) { require(paramsHashes[e3Id] != bytes32(0), E3DoesNotExist()); bytes32 inputRoot = bytes32(inputs[e3Id]._root()); bytes memory journal = new bytes(396); // (32 + 1) * 4 * 3 diff --git a/templates/default/deploy/default.ts b/templates/default/deploy/default.ts index 7805afe11..b1c7747c7 100644 --- a/templates/default/deploy/default.ts +++ b/templates/default/deploy/default.ts @@ -4,12 +4,11 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import { readDeploymentArgs, storeDeploymentArgs, updateE3Config } from '@enclave-e3/contracts/scripts' +import { getDeploymentChain, readDeploymentArgs, storeDeploymentArgs, updateE3Config } from '@enclave-e3/contracts/scripts' import { Enclave__factory as EnclaveFactory } from '@enclave-e3/contracts/types' +import { ensureTemplateCwd, ENCLAVE_CONFIG_FILE } from '../scripts/template-paths' import { MyProgram__factory as MyProgramFactory } from '../types/factories/contracts' import hre from 'hardhat' -import path from 'path' -import { fileURLToPath } from 'url' // Map contract names to config keys const contractMapping: Record = { @@ -20,15 +19,12 @@ const contractMapping: Record = { MockUSDC: 'fee_token', } -// Get __dirname equivalent in ES modules -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - export const deployTemplate = async () => { + ensureTemplateCwd() const { ethers } = await hre.network.connect() const [owner] = await ethers.getSigners() - const chain = hre.globalOptions.network + const chain = getDeploymentChain(hre) const enclaveAddress = readDeploymentArgs('Enclave', chain)?.address if (!enclaveAddress) { @@ -68,10 +64,15 @@ export const deployTemplate = async () => { const e3Program = await e3ProgramFactory.deploy(await enclave.getAddress(), await verifier.getAddress(), programId) await e3Program.waitForDeployment() - const tx = await enclave.enableE3Program(await e3Program.getAddress()) - + const programAddress = await e3Program.getAddress() + const tx = await enclave.enableE3Program(programAddress) await tx.wait() + const allowed = await enclave.e3Programs(programAddress) + if (!allowed) { + throw new Error(`MyProgram ${programAddress} was not enabled on Enclave ${enclaveAddress}`) + } + console.log("E3 Program enabled for Enclave's template") console.log( @@ -90,6 +91,5 @@ export const deployTemplate = async () => { chain, ) - // this expects you to run it from CRISP's root - updateE3Config(chain, path.join(__dirname, '..', 'enclave.config.yaml'), contractMapping) + updateE3Config(chain, ENCLAVE_CONFIG_FILE, contractMapping) } diff --git a/templates/default/deployed_contracts.json b/templates/default/deployed_contracts.json index 2c05d1975..e87cb845f 100644 --- a/templates/default/deployed_contracts.json +++ b/templates/default/deployed_contracts.json @@ -1,21 +1,21 @@ { "localhost": { "PoseidonT3": { - "blockNumber": 17, + "blockNumber": 5, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 18, + "blockNumber": 6, "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 19, + "blockNumber": 7, "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveTicketToken": { @@ -24,14 +24,14 @@ "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 21, + "blockNumber": 9, "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }, "SlashingManager": { "constructorArgs": { "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 22, + "blockNumber": 11, "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" }, "CiphernodeRegistryOwnable": { @@ -46,7 +46,7 @@ "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" }, - "blockNumber": 23, + "blockNumber": 12, "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" }, "BondingRegistry": { @@ -68,7 +68,7 @@ "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" }, - "blockNumber": 24, + "blockNumber": 13, "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" }, "Enclave": { @@ -88,7 +88,7 @@ "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" }, - "blockNumber": 27, + "blockNumber": 16, "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "E3RefundManager": { @@ -104,52 +104,32 @@ "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" }, - "blockNumber": 29, + "blockNumber": 18, "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, "MockComputeProvider": { - "blockNumber": 31, + "blockNumber": 39, "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" }, "MockDecryptionVerifier": { - "blockNumber": 32, + "blockNumber": 41, "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" }, "MockPkVerifier": { - "blockNumber": 33, + "blockNumber": 42, "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" }, "MockE3Program": { - "blockNumber": 34, + "blockNumber": 43, "address": "0x851356ae760d987E095750cCeb3bC6014560891C" }, - "ZKTranscriptLib": { - "blockNumber": 36, - "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" - }, - "DecryptionAggregatorVerifier": { - "blockNumber": 37, - "address": "0x998abeb3E57409262aE5b751f60747921B33613E" - }, - "DkgAggregatorVerifier": { - "blockNumber": 38, - "address": "0x70e0bA845a1A0F2DA3359C97E0285013525FFC49" - }, - "BfvDecryptionVerifier": { - "blockNumber": 39, - "address": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528" - }, - "BfvPkVerifier": { - "blockNumber": 41, - "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" - }, "ImageID": { - "address": "0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00", - "blockNumber": 44 + "address": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf", + "blockNumber": 49 }, "MyProgram": { - "address": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570", - "blockNumber": 46 + "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF", + "blockNumber": 51 } } } \ No newline at end of file diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index ee6c4d11d..1431f6fb3 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -4,22 +4,22 @@ chains: contracts: enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 17 + deploy_block: 16 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 13 + deploy_block: 12 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 14 + deploy_block: 13 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" deploy_block: 12 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 8 + deploy_block: 6 e3_program: - address: "0x851356ae760d987E095750cCeb3bC6014560891C" - deploy_block: 24 + address: "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" + deploy_block: 51 program: dev: true nodes: diff --git a/templates/default/hardhat.config.ts b/templates/default/hardhat.config.ts index 7e4816eeb..4feb17f7a 100644 --- a/templates/default/hardhat.config.ts +++ b/templates/default/hardhat.config.ts @@ -83,6 +83,16 @@ const config: HardhatUserConfig = { type: 'edr-simulated', chainType: 'l1', }, + localhost: { + accounts: { + mnemonic, + }, + chainId: chainIds.hardhat, + url: 'http://localhost:8545', + type: 'http', + chainType: 'l1', + timeout: 60000, + }, ganache: { accounts: { mnemonic, diff --git a/templates/default/package.json b/templates/default/package.json index 9f6d90b9b..f7d987c0e 100644 --- a/templates/default/package.json +++ b/templates/default/package.json @@ -15,7 +15,7 @@ "dev:frontend": "./scripts/dev_frontend.sh", "dev:program": "./scripts/dev_program.sh", "dev:server": "./scripts/dev_server.sh", - "predev:all": "[ ! -f './contracts/ImageID.sol' ] && enclave program compile || true", + "predev:all": "[ ! -f './.enclave/generated/contracts/ImageID.sol' ] && enclave program compile || true", "test": "hardhat test", "test:integration": "./scripts/test_integration.sh" }, diff --git a/templates/default/remappings.txt b/templates/default/remappings.txt new file mode 100644 index 000000000..f9ea5eab6 --- /dev/null +++ b/templates/default/remappings.txt @@ -0,0 +1,5 @@ +risc0/=lib/risc0-ethereum/contracts/src/ +@enclave-e3/contracts/=node_modules/@enclave-e3/contracts/ +@zk-kit/lazy-imt.sol/=node_modules/@zk-kit/lazy-imt.sol/ +poseidon-solidity/=node_modules/poseidon-solidity/ +@openzeppelin/=node_modules/@openzeppelin/ diff --git a/templates/default/scripts/anvil-automine.mjs b/templates/default/scripts/anvil-automine.mjs new file mode 100644 index 000000000..28f050f55 --- /dev/null +++ b/templates/default/scripts/anvil-automine.mjs @@ -0,0 +1,32 @@ +// Keeps anvil block.timestamp moving during local integration (finalizeCommittee needs +// block.timestamp > committeeDeadline; eth_call-only retries do not mine blocks). + +const RPC = process.env.ANVIL_RPC_URL ?? 'http://127.0.0.1:8545' + +async function rpc(method, params = []) { + const res = await fetch(RPC, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }), + }) + if (!res.ok) { + throw new Error(`RPC ${method} HTTP ${res.status}`) + } + const json = await res.json() + if (json.error) { + throw new Error(json.error.message ?? JSON.stringify(json.error)) + } +} + +async function loop() { + for (;;) { + try { + await rpc('evm_mine') + } catch { + // anvil not up yet + } + await new Promise((r) => setTimeout(r, 1000)) + } +} + +loop() diff --git a/templates/default/scripts/deploy-local.ts b/templates/default/scripts/deploy-local.ts index d5ec6e610..059d27558 100644 --- a/templates/default/scripts/deploy-local.ts +++ b/templates/default/scripts/deploy-local.ts @@ -6,8 +6,10 @@ import { deployEnclave } from '@enclave-e3/contracts/scripts' import { deployTemplate } from '../deploy/default' +import { ensureTemplateCwd } from './template-paths' async function main() { + ensureTemplateCwd() console.log('🚀 Deploying Enclave protocol locally...') // Get hardhat runtime environment @@ -20,10 +22,12 @@ async function main() { console.log('Deploying with account:', deployer.address) console.log('Account balance:', ethers.formatEther(await ethers.provider.getBalance(deployer.address))) - // Execute the deployment - await deployEnclave(true, true) + // Mocks for local dev; skip on-chain ZK verifiers (needs pnpm compile:circuits). + await deployEnclave(true, false) await deployTemplate() } -// Execute the deployment -main().catch(console.error) +main().catch((err) => { + console.error(err) + process.exit(1) +}) diff --git a/templates/default/scripts/dev_ciphernodes.sh b/templates/default/scripts/dev_ciphernodes.sh index 37133c1da..62e5e196a 100755 --- a/templates/default/scripts/dev_ciphernodes.sh +++ b/templates/default/scripts/dev_ciphernodes.sh @@ -2,6 +2,8 @@ set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")/.." + SIGNAL_FILE=/tmp/enclave_ciphernodes_ready cleanup() { @@ -20,7 +22,12 @@ trap cleanup INT TERM echo "Waiting for local evm node..." pnpm wait-on tcp:localhost:8545 -# nuke past installations as we are adding these nodes to the contract +if [ ! -f './.enclave/generated/contracts/ImageID.sol' ]; then + echo "Compiling guest program (ImageID)..." + enclave program compile +fi + +# Fresh node state for this deploy rm -rf .enclave/data rm -rf .enclave/config @@ -39,10 +46,14 @@ enclave wallet set --name cn5 --private-key "$PRIVATE_KEY_CN5" echo "Setting up ZK prover..." enclave noir setup -# using & instead of -d so that wait works below -enclave nodes up -v & - -sleep 2 +# Deploy before starting nodes so enclave.config.yaml addresses match the chain. +echo "Deploying protocol + MyProgram..." +pnpm exec hardhat utils:clean-deployments --network localhost +pnpm exec hardhat run scripts/deploy-local.ts --network localhost +if ! grep -q '"MyProgram"' deployed_contracts.json; then + echo "deployTemplate did not record MyProgram — check deploy logs above" + exit 1 +fi CN1=$(grep -A 1 'cn1:' enclave.config.yaml | grep 'address:' | sed 's/.*address: *"\([^"]*\)".*/\1/') CN2=$(grep -A 1 'cn2:' enclave.config.yaml | grep 'address:' | sed 's/.*address: *"\([^"]*\)".*/\1/') @@ -50,8 +61,10 @@ CN3=$(grep -A 1 'cn3:' enclave.config.yaml | grep 'address:' | sed 's/.*address: CN4=$(grep -A 1 'cn4:' enclave.config.yaml | grep 'address:' | sed 's/.*address: *"\([^"]*\)".*/\1/') CN5=$(grep -A 1 'cn5:' enclave.config.yaml | grep 'address:' | sed 's/.*address: *"\([^"]*\)".*/\1/') -# Add ciphernodes using variables from config.sh -pnpm run deploy && sleep 2 +echo "Starting ciphernodes (post-deploy config)..." +enclave nodes up -v & + +sleep 4 pnpm hardhat ciphernode:admin-add --ciphernode-address $CN1 --network localhost pnpm hardhat ciphernode:admin-add --ciphernode-address $CN2 --network localhost diff --git a/templates/default/scripts/template-paths.ts b/templates/default/scripts/template-paths.ts new file mode 100644 index 000000000..7d7f7a76b --- /dev/null +++ b/templates/default/scripts/template-paths.ts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// Keeps Hardhat deploy scripts scoped to this template directory only. + +import path from 'path' +import { fileURLToPath } from 'url' + +const scriptsDir = path.dirname(fileURLToPath(import.meta.url)) + +/** Absolute path to `templates/default`. */ +export const TEMPLATE_ROOT = path.resolve(scriptsDir, '..') + +export const DEPLOYMENTS_FILE = path.join(TEMPLATE_ROOT, 'deployed_contracts.json') +export const ENCLAVE_CONFIG_FILE = path.join(TEMPLATE_ROOT, 'enclave.config.yaml') + +/** Pin cwd so `@enclave-e3/contracts` deployment helpers write only under the template. */ +export function ensureTemplateCwd(): void { + if (process.cwd() !== TEMPLATE_ROOT) { + process.chdir(TEMPLATE_ROOT) + } +} diff --git a/templates/default/scripts/test_integration.sh b/templates/default/scripts/test_integration.sh index 171f776cd..6622e3bdd 100755 --- a/templates/default/scripts/test_integration.sh +++ b/templates/default/scripts/test_integration.sh @@ -2,6 +2,8 @@ set -euo pipefail +cd "$(dirname "${BASH_SOURCE[0]}")/.." + passed_message() { echo "" echo "------------------------" @@ -19,14 +21,14 @@ failed_message() { exit 1 } -export $(enclave print-env --chain localhost) (pnpm concurrently \ - --names "TEST,EVM,CIPHER,SERVER,PROGRAM" \ - --prefix-colors "blue,cyan,magenta,yellow,green" \ + --names "TEST,EVM,MINE,CIPHER,SERVER,PROGRAM" \ + --prefix-colors "blue,cyan,gray,magenta,yellow,green" \ --kill-others \ --success first \ - "wait-on http://localhost:13151/health && pnpm vitest run ./tests/integration.spec.ts" \ - "anvil --host 0.0.0.0 --chain-id 31337 --block-time 1 --mnemonic 'test test test test test test test test test test test junk' --silent" \ + "wait-on file:/tmp/enclave_ciphernodes_ready tcp:localhost:8545 http://localhost:13151/health && export \$(enclave print-env --chain localhost) && pnpm vitest run ./tests/integration.spec.ts" \ + "anvil --host 0.0.0.0 --chain-id 31337 --mnemonic 'test test test test test test test test test test test junk' --silent" \ + "wait-on tcp:localhost:8545 && node ./scripts/anvil-automine.mjs" \ "pnpm dev:ciphernodes" \ "TEST_MODE=1 pnpm dev:server" \ "pnpm dev:program" && passed_message) || failed_message diff --git a/templates/default/tests/anvil-helpers.ts b/templates/default/tests/anvil-helpers.ts new file mode 100644 index 000000000..64c7cb42d --- /dev/null +++ b/templates/default/tests/anvil-helpers.ts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: LGPL-3.0-only + +import type { PublicClient } from 'viem' + +/** Advance anvil time and mine (closes sortition submission window on-chain). */ +export async function advanceAnvilTime(publicClient: PublicClient, seconds: number): Promise { + await publicClient.request({ + method: 'evm_increaseTime', + params: [seconds], + }) + await publicClient.request({ + method: 'evm_mine', + params: [], + }) +} + +export function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) +} diff --git a/templates/default/tests/integration.spec.ts b/templates/default/tests/integration.spec.ts index dd4c73536..6001b9792 100644 --- a/templates/default/tests/integration.spec.ts +++ b/templates/default/tests/integration.spec.ts @@ -25,6 +25,8 @@ import { publishInput } from '../server/input' import { privateKeyToAccount } from 'viem/accounts' import { anvil } from 'viem/chains' +import { advanceAnvilTime, sleep } from './anvil-helpers' + export function getContractAddresses() { return { enclave: process.env.ENCLAVE_ADDRESS as `0x${string}`, @@ -189,7 +191,8 @@ describe('Integration', () => { const { waitForEvent } = await setupEventListeners(sdk, store) const committeeSize = CommitteeSize.Micro - const duration = 1000 + // Input window length (seconds); also used as vitest wait budget per phase. + const duration = 120 const inputWindow = await calculateInputWindow(publicClient, duration) const computeProviderParams = encodeComputeProviderParams( DEFAULT_COMPUTE_PROVIDER_PARAMS, @@ -217,7 +220,7 @@ describe('Integration', () => { const hash = await sdk.approveFeeToken(quote) console.log('Fee token approved:', hash) - await new Promise((resolve) => setTimeout(resolve, 1000)) + await sleep(1000) // REQUEST phase const timeoutMs = duration * 1000 @@ -241,7 +244,15 @@ describe('Integration', () => { const stageAfterRequest = await sdk.getE3Stage(state.e3Id) assert.strictEqual(stageAfterRequest, E3Stage.Requested, 'E3 stage should be Requested after requestE3') + // Ciphernodes submit tickets during the on-chain sortition window (10s). Anvil must mine so + // block.timestamp passes committeeDeadline before finalizeCommittee (see anvil-automine.mjs). + console.log('Waiting for ciphernode sortition tickets...') + await sleep(8000) + await advanceAnvilTime(publicClient, 15) + console.log('Advanced anvil past sortition deadline') + // Ciphernodes will publish a public key within the COMMITTEE_PUBLISHED event + console.log('Waiting for committee + DKG (CommitteePublished)...') event = await waitForEvent(RegistryEventType.COMMITTEE_PUBLISHED, undefined, timeoutMs) const publicKeyBytes = hexToBytes(event.data.publicKey as `0x${string}`) From 9e3dcc55c3317d34a8aa82858036f273c0699d38 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 21:39:19 +0200 Subject: [PATCH 26/54] fix integration --- crates/tests/tests/integration.rs | 721 ++++++++++++++++-------------- 1 file changed, 397 insertions(+), 324 deletions(-) diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 2929616fb..e03bc4830 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -7,7 +7,7 @@ use actix::Actor; use alloy::primitives::{Address, FixedBytes, I256, U256}; use alloy::signers::local::PrivateKeySigner; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use e3_bfv_client::decode_bytes_to_vec_u64; use e3_ciphernode_builder::{CiphernodeBuilder, EventSystem}; use e3_config::BBPath; @@ -155,6 +155,29 @@ impl Drop for EnvTimeoutVarsGuard { } } +async fn copy_dir_recursive(src: &std::path::Path, dst: &std::path::Path) -> Result<()> { + tokio::fs::create_dir_all(dst).await?; + let mut entries = tokio::fs::read_dir(src).await?; + while let Some(entry) = entries.next_entry().await? { + let file_type = entry.file_type().await?; + let dest = dst.join(entry.file_name()); + if file_type.is_dir() { + Box::pin(copy_dir_recursive(&entry.path(), &dest)).await?; + } else { + tokio::fs::copy(entry.path(), &dest) + .await + .with_context(|| { + format!( + "copy circuit artifact {} -> {}", + entry.path().display(), + dest.display() + ) + })?; + } + } + Ok(()) +} + /// Create a ZkBackend for integration tests. /// If a local bb binary is found, uses it with fixture files (fast path). /// Otherwise, calls `ensure_installed()` to download bb + circuits (CI path). @@ -167,6 +190,10 @@ async fn setup_test_zk_backend( let bb_binary = noir_dir.join("bin").join("bb"); let circuits_dir = noir_dir.join("circuits"); let work_dir = noir_dir.join("work").join("test_node"); + let repo_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join(".."); + let dist_preset = repo_root.join("dist/circuits").join(preset_subdir); if let Some(bb) = find_bb().await { tokio::fs::create_dir_all(bb_binary.parent().unwrap()) @@ -180,335 +207,362 @@ async fn setup_test_zk_backend( #[cfg(not(unix))] compile_error!("Integration tests require unix symlink support"); - let circuits_build_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("..") - .join("circuits") - .join("bin"); - let dkg_target = circuits_build_root.join("dkg").join("target"); - let threshold_target = circuits_build_root.join("threshold").join("target"); - let c3_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c3_fold") - .join("target"); - let c3_fold_kernel_target = circuits_build_root - .join("recursive_aggregation") - .join("c3_fold_kernel") - .join("target"); - let c6_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c6_fold") - .join("target"); - let c6_fold_kernel_target = circuits_build_root - .join("recursive_aggregation") - .join("c6_fold_kernel") - .join("target"); - let c2ab_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c2ab_fold") - .join("target"); - let c3ab_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c3ab_fold") - .join("target"); - let c4ab_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("c4ab_fold") - .join("target"); - let node_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("node_fold") - .join("target"); - let nodes_fold_target = circuits_build_root - .join("recursive_aggregation") - .join("nodes_fold") - .join("target"); - let nodes_fold_kernel_target = circuits_build_root - .join("recursive_aggregation") - .join("nodes_fold_kernel") - .join("target"); - let dkg_aggregator_target = circuits_build_root - .join("recursive_aggregation") - .join("dkg_aggregator") - .join("target"); - let decryption_aggregator_target = circuits_build_root - .join("recursive_aggregation") - .join("decryption_aggregator") - .join("target"); - - // Helper: copy {name}.json + VK artifacts into a destination directory. - // vk_suffix/vk_hash_suffix select the source VK flavor: - // ".vk_noir" / ".vk_noir_hash" → Recursive variant (inner proofs) - // ".vk_recursive" / ".vk_recursive_hash" → Default variant (wrapper/fold proofs) - // ".vk" / ".vk_hash" → EVM variant - async fn copy_circuit( - src_dir: &std::path::Path, - dst_dir: &std::path::Path, - name: &str, - vk_suffix: &str, - vk_hash_suffix: &str, - ) { - tokio::fs::create_dir_all(dst_dir).await.unwrap(); - tokio::fs::copy( - src_dir.join(format!("{name}.json")), - dst_dir.join(format!("{name}.json")), + let preset_out = circuits_dir.join(preset_subdir); + let dist_marker = dist_preset.join("recursive/dkg/pk/pk.json"); + let circuits_bin_marker = repo_root.join("circuits/bin/dkg/target/pk.json"); + + if dist_marker.exists() { + copy_dir_recursive(&dist_preset, &preset_out).await?; + } else if !circuits_bin_marker.exists() { + // CI `rust_integration_tests` does not run `pnpm build:circuits`; download the + // pinned release tarball when no local fixtures are present. + println!( + "No local circuit fixtures under dist/circuits or circuits/bin; \ + downloading release circuits via ensure_installed()..." + ); + let backend = ZkBackend::new(BBPath::Default(bb_binary), circuits_dir, work_dir); + backend + .ensure_installed() + .await + .context("download ZK circuits for integration tests")?; + return Ok((backend, temp)); + } else { + let circuits_build_root = repo_root.join("circuits").join("bin"); + let dkg_target = circuits_build_root.join("dkg").join("target"); + let threshold_target = circuits_build_root.join("threshold").join("target"); + let c3_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c3_fold") + .join("target"); + let c3_fold_kernel_target = circuits_build_root + .join("recursive_aggregation") + .join("c3_fold_kernel") + .join("target"); + let c6_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c6_fold") + .join("target"); + let c6_fold_kernel_target = circuits_build_root + .join("recursive_aggregation") + .join("c6_fold_kernel") + .join("target"); + let c2ab_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c2ab_fold") + .join("target"); + let c3ab_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c3ab_fold") + .join("target"); + let c4ab_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("c4ab_fold") + .join("target"); + let node_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("node_fold") + .join("target"); + let nodes_fold_target = circuits_build_root + .join("recursive_aggregation") + .join("nodes_fold") + .join("target"); + let nodes_fold_kernel_target = circuits_build_root + .join("recursive_aggregation") + .join("nodes_fold_kernel") + .join("target"); + let dkg_aggregator_target = circuits_build_root + .join("recursive_aggregation") + .join("dkg_aggregator") + .join("target"); + let decryption_aggregator_target = circuits_build_root + .join("recursive_aggregation") + .join("decryption_aggregator") + .join("target"); + + // Helper: copy {name}.json + VK artifacts into a destination directory. + // vk_suffix/vk_hash_suffix select the source VK flavor: + // ".vk_noir" / ".vk_noir_hash" → Recursive variant (inner proofs) + // ".vk_recursive" / ".vk_recursive_hash" → Default variant (wrapper/fold proofs) + // ".vk" / ".vk_hash" → EVM variant + async fn copy_circuit( + src_dir: &std::path::Path, + dst_dir: &std::path::Path, + name: &str, + vk_suffix: &str, + vk_hash_suffix: &str, + ) -> Result<()> { + tokio::fs::create_dir_all(dst_dir).await?; + let copy_file = |src: std::path::PathBuf, dst: std::path::PathBuf| async move { + tokio::fs::copy(&src, &dst).await.with_context(|| { + format!( + "copy circuit artifact {} -> {}", + src.display(), + dst.display() + ) + }) + }; + copy_file( + src_dir.join(format!("{name}.json")), + dst_dir.join(format!("{name}.json")), + ) + .await?; + copy_file( + src_dir.join(format!("{name}{vk_suffix}")), + dst_dir.join(format!("{name}.vk")), + ) + .await?; + let vk_hash_src = src_dir.join(format!("{name}{vk_hash_suffix}")); + let vk_hash_src = if tokio::fs::try_exists(&vk_hash_src).await? { + vk_hash_src + } else { + // `bb write_vk` leaves `vk_hash` in some aggregation target dirs. + src_dir.join("vk_hash") + }; + copy_file(vk_hash_src, dst_dir.join(format!("{name}.vk_hash"))).await?; + Ok(()) + } + + // ── recursive/ variant (inner/base proofs, uses .vk_noir) ────────── + let preset_dir = circuits_dir.join(preset_subdir); + + let rv = preset_dir.join("recursive"); + + // T0 (pk) + copy_circuit( + &dkg_target, + &rv.join("dkg/pk"), + "pk", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - tokio::fs::copy( - src_dir.join(format!("{name}{vk_suffix}")), - dst_dir.join(format!("{name}.vk")), + .await?; + // C1 (pk_generation) + copy_circuit( + &threshold_target, + &rv.join("threshold/pk_generation"), + "pk_generation", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - tokio::fs::copy( - src_dir.join(format!("{name}{vk_hash_suffix}")), - dst_dir.join(format!("{name}.vk_hash")), + .await?; + // C2a (sk_share_computation) + copy_circuit( + &dkg_target, + &rv.join("dkg/sk_share_computation"), + "sk_share_computation", + ".vk_noir", + ".vk_noir_hash", ) - .await - .unwrap(); - } - - // ── recursive/ variant (inner/base proofs, uses .vk_noir) ────────── - let preset_dir = circuits_dir.join(preset_subdir); + .await?; + // C2b (e_sm_share_computation) + copy_circuit( + &dkg_target, + &rv.join("dkg/e_sm_share_computation"), + "e_sm_share_computation", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C3 (share_encryption) + copy_circuit( + &dkg_target, + &rv.join("dkg/share_encryption"), + "share_encryption", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C4 (dkg/share_decryption) + copy_circuit( + &dkg_target, + &rv.join("dkg/share_decryption"), + "share_decryption", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C5 (pk_aggregation) + copy_circuit( + &threshold_target, + &rv.join("threshold/pk_aggregation"), + "pk_aggregation", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C6 (threshold/share_decryption) + copy_circuit( + &threshold_target, + &rv.join("threshold/share_decryption"), + "share_decryption", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; + // C7 (decrypted_shares_aggregation) + copy_circuit( + &threshold_target, + &rv.join("threshold/decrypted_shares_aggregation"), + "decrypted_shares_aggregation", + ".vk_noir", + ".vk_noir_hash", + ) + .await?; - let rv = preset_dir.join("recursive"); + // ── default/ variant (recursive aggregation bins, uses .vk_recursive) ─── - // T0 (pk) - copy_circuit( - &dkg_target, - &rv.join("dkg/pk"), - "pk", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C1 (pk_generation) - copy_circuit( - &threshold_target, - &rv.join("threshold/pk_generation"), - "pk_generation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C2a (sk_share_computation) - copy_circuit( - &dkg_target, - &rv.join("dkg/sk_share_computation"), - "sk_share_computation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C2b (e_sm_share_computation) - copy_circuit( - &dkg_target, - &rv.join("dkg/e_sm_share_computation"), - "e_sm_share_computation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C3 (share_encryption) - copy_circuit( - &dkg_target, - &rv.join("dkg/share_encryption"), - "share_encryption", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C4 (dkg/share_decryption) - copy_circuit( - &dkg_target, - &rv.join("dkg/share_decryption"), - "share_decryption", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C5 (pk_aggregation) - copy_circuit( - &threshold_target, - &rv.join("threshold/pk_aggregation"), - "pk_aggregation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C6 (threshold/share_decryption) - copy_circuit( - &threshold_target, - &rv.join("threshold/share_decryption"), - "share_decryption", - ".vk_noir", - ".vk_noir_hash", - ) - .await; - // C7 (decrypted_shares_aggregation) - copy_circuit( - &threshold_target, - &rv.join("threshold/decrypted_shares_aggregation"), - "decrypted_shares_aggregation", - ".vk_noir", - ".vk_noir_hash", - ) - .await; + let dv = preset_dir.join("default"); - // ── default/ variant (recursive aggregation bins, uses .vk_recursive) ─── - - let dv = preset_dir.join("default"); + // C5 (pk_aggregation) — proven with noir-recursive-no-zk and folded into + // DkgAggregator, so it must be staged under default/ too. + copy_circuit( + &threshold_target, + &dv.join("threshold/pk_aggregation"), + "pk_aggregation", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; - // C5 (pk_aggregation) — proven with noir-recursive-no-zk and folded into - // DkgAggregator, so it must be staged under default/ too. - copy_circuit( - &threshold_target, - &dv.join("threshold/pk_aggregation"), - "pk_aggregation", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - - copy_circuit( - &c3_fold_target, - &dv.join("recursive_aggregation/c3_fold"), - "c3_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c3_fold_kernel_target, - &dv.join("recursive_aggregation/c3_fold_kernel"), - "c3_fold_kernel", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c6_fold_target, - &dv.join("recursive_aggregation/c6_fold"), - "c6_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c6_fold_kernel_target, - &dv.join("recursive_aggregation/c6_fold_kernel"), - "c6_fold_kernel", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c2ab_fold_target, - &dv.join("recursive_aggregation/c2ab_fold"), - "c2ab_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c3ab_fold_target, - &dv.join("recursive_aggregation/c3ab_fold"), - "c3ab_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &c4ab_fold_target, - &dv.join("recursive_aggregation/c4ab_fold"), - "c4ab_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &node_fold_target, - &dv.join("recursive_aggregation/node_fold"), - "node_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &nodes_fold_target, - &dv.join("recursive_aggregation/nodes_fold"), - "nodes_fold", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &nodes_fold_kernel_target, - &dv.join("recursive_aggregation/nodes_fold_kernel"), - "nodes_fold_kernel", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &dkg_aggregator_target, - &dv.join("recursive_aggregation/dkg_aggregator"), - "dkg_aggregator", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - copy_circuit( - &decryption_aggregator_target, - &dv.join("recursive_aggregation/decryption_aggregator"), - "decryption_aggregator", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; - // C7 (decrypted_shares_aggregation) — proven with noir-recursive-no-zk and - // folded into DecryptionAggregator, so it must also be staged under default/. - copy_circuit( - &threshold_target, - &dv.join("threshold/decrypted_shares_aggregation"), - "decrypted_shares_aggregation", - ".vk_recursive", - ".vk_recursive_hash", - ) - .await; + copy_circuit( + &c3_fold_target, + &dv.join("recursive_aggregation/c3_fold"), + "c3_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c3_fold_kernel_target, + &dv.join("recursive_aggregation/c3_fold_kernel"), + "c3_fold_kernel", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c6_fold_target, + &dv.join("recursive_aggregation/c6_fold"), + "c6_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c6_fold_kernel_target, + &dv.join("recursive_aggregation/c6_fold_kernel"), + "c6_fold_kernel", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c2ab_fold_target, + &dv.join("recursive_aggregation/c2ab_fold"), + "c2ab_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c3ab_fold_target, + &dv.join("recursive_aggregation/c3ab_fold"), + "c3ab_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &c4ab_fold_target, + &dv.join("recursive_aggregation/c4ab_fold"), + "c4ab_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &node_fold_target, + &dv.join("recursive_aggregation/node_fold"), + "node_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &nodes_fold_target, + &dv.join("recursive_aggregation/nodes_fold"), + "nodes_fold", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &nodes_fold_kernel_target, + &dv.join("recursive_aggregation/nodes_fold_kernel"), + "nodes_fold_kernel", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &dkg_aggregator_target, + &dv.join("recursive_aggregation/dkg_aggregator"), + "dkg_aggregator", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + copy_circuit( + &decryption_aggregator_target, + &dv.join("recursive_aggregation/decryption_aggregator"), + "decryption_aggregator", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; + // C7 (decrypted_shares_aggregation) — proven with noir-recursive-no-zk and + // folded into DecryptionAggregator, so it must also be staged under default/. + copy_circuit( + &threshold_target, + &dv.join("threshold/decrypted_shares_aggregation"), + "decrypted_shares_aggregation", + ".vk_recursive", + ".vk_recursive_hash", + ) + .await?; - // ── evm/ variant (on-chain verification: DKG aggregator, C7) ─────────── + // ── evm/ variant (on-chain verification: DKG aggregator, C7) ─────────── - let ev = preset_dir.join("evm"); + let ev = preset_dir.join("evm"); - // DKG aggregator — EVM-targeted (folds + C5 verified inside) - copy_circuit( - &dkg_aggregator_target, - &ev.join("recursive_aggregation/dkg_aggregator"), - "dkg_aggregator", - ".vk", - ".vk_hash", - ) - .await; - // Decryption aggregator — EVM-targeted (C6 fold + C7 verified inside) - copy_circuit( - &decryption_aggregator_target, - &ev.join("recursive_aggregation/decryption_aggregator"), - "decryption_aggregator", - ".vk", - ".vk_hash", - ) - .await; - // C7 (decrypted_shares_aggregation) — EVM-targeted - copy_circuit( - &threshold_target, - &ev.join("threshold/decrypted_shares_aggregation"), - "decrypted_shares_aggregation", - ".vk", - ".vk_hash", - ) - .await; + // DKG aggregator — EVM-targeted (folds + C5 verified inside) + copy_circuit( + &dkg_aggregator_target, + &ev.join("recursive_aggregation/dkg_aggregator"), + "dkg_aggregator", + ".vk", + ".vk_hash", + ) + .await?; + // Decryption aggregator — EVM-targeted (C6 fold + C7 verified inside) + copy_circuit( + &decryption_aggregator_target, + &ev.join("recursive_aggregation/decryption_aggregator"), + "decryption_aggregator", + ".vk", + ".vk_hash", + ) + .await?; + // C7 (decrypted_shares_aggregation) — EVM-targeted + copy_circuit( + &threshold_target, + &ev.join("threshold/decrypted_shares_aggregation"), + "decrypted_shares_aggregation", + ".vk", + ".vk_hash", + ) + .await?; + } let backend = ZkBackend::new(BBPath::Default(bb_binary), circuits_dir, work_dir); @@ -638,6 +692,27 @@ fn determine_committee( Ok((committee, committee_scores, buffer_nodes)) } +/// Lowest-address committee member after `CommitteeFinalized::sort_by_score` (party 0 / active aggregator). +fn active_aggregator_address( + committee: &[String], + scores: &[String], + e3_id: &E3id, + chain_id: u64, +) -> String { + let mut finalized = CommitteeFinalized { + e3_id: e3_id.clone(), + committee: committee.to_vec(), + scores: scores.to_vec(), + chain_id, + }; + finalized.sort_by_score(); + finalized + .committee + .first() + .cloned() + .expect("committee must be non-empty") +} + fn find_node_index_by_address(nodes: &CiphernodeSystem, address: &str) -> Result { for (index, node) in nodes.iter().enumerate() { if node.address().eq_ignore_ascii_case(address) { @@ -1117,10 +1192,8 @@ async fn test_trbfv_actor() -> Result<()> { buffer_nodes.len() )); - let active_aggregator_addr = committee - .first() - .cloned() - .expect("committee should have an active aggregator"); + let active_aggregator_addr = + active_aggregator_address(&committee, &committee_scores, &e3_id, chain_id); let active_aggregator_index = find_node_index_by_address(&nodes, &active_aggregator_addr)?; println!( From d7f46fb2801f49ebb5033c740dab17da6419bf17 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Thu, 21 May 2026 22:40:01 +0200 Subject: [PATCH 27/54] validate datahashes with actual evidence; binding node check against topnode --- .../accusation_quorum_reached.rs | 11 +- .../enclave_event/commitment_consistency.rs | 17 ++- .../proof_verification_passed.rs | 7 + crates/evm/src/slashing_manager_sol_writer.rs | 15 ++- .../src/actors/accusation_manager.rs | 61 ++++++++- .../actors/commitment_consistency_checker.rs | 55 +++++--- crates/zk-prover/src/actors/proof_request.rs | 1 + .../src/actors/proof_verification.rs | 1 + .../src/actors/share_verification.rs | 35 ++++- .../tests/slashing_integration_tests.rs | 54 ++++++-- docs/pages/cryptography.mdx | 48 +++++-- docs/pages/internals/dkg.mdx | 34 ++++- .../contracts/Enclave.sol/Enclave.json | 2 +- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 42 +++++- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../ISlashingManager.json | 2 +- .../CiphernodeRegistryOwnable.json | 66 ++++++++-- .../interfaces/ICiphernodeRegistry.sol | 17 +++ .../registry/CiphernodeRegistryOwnable.sol | 10 ++ .../contracts/slashing/SlashingManager.sol | 121 ++++++++++++------ .../contracts/test/MockCiphernodeRegistry.sol | 19 +++ .../verifiers/DkgFoldAttestationVerifier.sol | 14 ++ .../test/BfvVkBindingIntegration.spec.ts | 10 +- .../enclave-contracts/test/Enclave.spec.ts | 17 ++- .../test/Slashing/CommitteeExpulsion.spec.ts | 37 +++++- .../test/Slashing/SlashingManager.spec.ts | 12 +- .../test/fixtures/attestation.ts | 18 ++- 28 files changed, 593 insertions(+), 137 deletions(-) diff --git a/crates/events/src/enclave_event/accusation_quorum_reached.rs b/crates/events/src/enclave_event/accusation_quorum_reached.rs index c4911859c..d1fe6fd1c 100644 --- a/crates/events/src/enclave_event/accusation_quorum_reached.rs +++ b/crates/events/src/enclave_event/accusation_quorum_reached.rs @@ -6,7 +6,7 @@ use crate::{AccusationVote, E3id, ProofType}; use actix::Message; -use alloy::primitives::Address; +use alloy::primitives::{Address, Bytes}; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; @@ -54,6 +54,15 @@ pub struct AccusationQuorumReached { pub votes_against: Vec, /// The quorum decision. pub outcome: AccusationOutcome, + /// Raw `abi.encode(proof.data, public_signals)` — preimage of every voter's + /// `data_hash`. The on-chain `SlashingManager.proposeSlash` recomputes + /// `keccak256(evidence)` and requires it to equal the common voter + /// `dataHash`, binding the votes to specific evidence bytes on-chain. + /// Empty when this node didn't have the raw bytes locally (e.g. consistency- + /// violation path); slashing still works in that case but without the + /// on-chain evidence binding. + #[serde(default)] + pub evidence: Bytes, } impl Display for AccusationQuorumReached { diff --git a/crates/events/src/enclave_event/commitment_consistency.rs b/crates/events/src/enclave_event/commitment_consistency.rs index 7cdcb2165..3ea2fa86d 100644 --- a/crates/events/src/enclave_event/commitment_consistency.rs +++ b/crates/events/src/enclave_event/commitment_consistency.rs @@ -14,7 +14,7 @@ //! check proceed to ZK verification. use crate::{CorrelationId, E3id, ProofType, VerificationKind}; -use alloy::primitives::Address; +use alloy::primitives::{Address, Bytes}; use e3_utils::utility_types::ArcBytes; use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; @@ -27,11 +27,13 @@ use std::collections::BTreeSet; pub struct PartyProofData { pub party_id: u64, pub address: Address, - /// Each entry is a `(proof_type, public_signals, data_hash)` tuple from a - /// signed proof. The `data_hash` is `keccak256(abi.encode(proof.data, + /// Each entry is a `(proof_type, public_signals, data_hash, proof_data)` tuple + /// from a signed proof. The `data_hash` is `keccak256(abi.encode(proof.data, /// public_signals))` — used for the accusation protocol if a consistency - /// violation is detected. - pub proofs: Vec<(ProofType, ArcBytes, [u8; 32])>, + /// violation is detected. The `proof_data` (raw `proof.data` bytes) is forwarded + /// alongside so the on-chain slashing contract can recompute the dataHash from + /// the evidence preimage `abi.encode(proof_data, public_signals)`. + pub proofs: Vec<(ProofType, ArcBytes, [u8; 32], ArcBytes)>, } /// Published by [`ShareVerificationActor`] after ECDSA validation, before ZK. @@ -80,4 +82,9 @@ pub struct CommitmentConsistencyViolation { /// `keccak256(abi.encode(proof.data, public_signals))` of the accused party's /// proof — matches the data_hash used by the accusation protocol. pub data_hash: [u8; 32], + /// Raw `abi.encode(proof.data, public_signals)` — preimage of `data_hash`. + /// Forwarded to `SlashingManager.proposeSlash` so the on-chain contract can + /// verify `keccak256(evidence) == dataHash` bound in voter signatures. + #[serde(default)] + pub evidence: Bytes, } diff --git a/crates/events/src/enclave_event/proof_verification_passed.rs b/crates/events/src/enclave_event/proof_verification_passed.rs index 15511f54e..b28d1a065 100644 --- a/crates/events/src/enclave_event/proof_verification_passed.rs +++ b/crates/events/src/enclave_event/proof_verification_passed.rs @@ -34,6 +34,13 @@ pub struct ProofVerificationPassed { pub data_hash: [u8; 32], /// Raw public signals from the verified proof — for commitment consistency checks. pub public_signals: ArcBytes, + /// Raw proof bytes — needed only by paths that may later turn this proof into + /// slashing evidence (so the on-chain contract can recompute and verify the + /// dataHash bound in voter signatures). Empty when the emitter did not have + /// the raw bytes available; downstream consumers that need it will fail + /// gracefully if so. + #[serde(default)] + pub proof_data: ArcBytes, } impl Display for ProofVerificationPassed { diff --git a/crates/evm/src/slashing_manager_sol_writer.rs b/crates/evm/src/slashing_manager_sol_writer.rs index 634f2f974..c3796ce6b 100644 --- a/crates/evm/src/slashing_manager_sol_writer.rs +++ b/crates/evm/src/slashing_manager_sol_writer.rs @@ -192,9 +192,11 @@ impl Handler /// Encode `AccusationQuorumReached` into the attestation evidence format expected /// by `SlashingManager.proposeSlash()`: -/// `abi.encode(uint256 proofType, address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures)` +/// `abi.encode(uint256 proofType, address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures, bytes evidence)` /// /// Voters are sorted ascending by address to satisfy the contract's duplicate-prevention check. +/// `evidence` is the `abi.encode(proof.data, public_signals)` preimage of `dataHash` — the contract +/// recomputes `keccak256(evidence)` and requires it to equal the common voter `dataHash`. fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Vec { // Collect and sort votes by voter address (ascending) let mut votes = data.votes_for.clone(); @@ -208,8 +210,17 @@ fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Vec { .iter() .map(|v| Bytes::from(v.signature.extract_bytes())) .collect(); + let evidence: Bytes = data.evidence.clone(); - (proof_type, voters, agrees, data_hashes, signatures).abi_encode_params() + ( + proof_type, + voters, + agrees, + data_hashes, + signatures, + evidence, + ) + .abi_encode_params() } async fn submit_slash_proposal( diff --git a/crates/zk-prover/src/actors/accusation_manager.rs b/crates/zk-prover/src/actors/accusation_manager.rs index 162a21e70..87d142a59 100644 --- a/crates/zk-prover/src/actors/accusation_manager.rs +++ b/crates/zk-prover/src/actors/accusation_manager.rs @@ -65,6 +65,13 @@ struct ReceivedProofData { data_hash: [u8; 32], /// `true` if our local verification passed, `false` if it failed. verification_passed: bool, + /// Raw `abi.encode(proof.data, proof.public_signals)` — preimage of + /// `data_hash`. Forwarded to the on-chain slashing contract so it can + /// recompute and verify the dataHash bound in voter signatures. Empty + /// only on paths where the raw bytes weren't available locally; those + /// paths can still slash, but they fall back to off-chain trust for + /// the evidence binding. + evidence: Bytes, } /// Tracks an in-flight ZK re-verification for a forwarded C3a/C3b proof. @@ -73,6 +80,9 @@ struct PendingReVerification { data_hash: [u8; 32], accused: Address, proof_type: ProofType, + /// Evidence preimage bytes from the forwarded proof, used to populate + /// `ReceivedProofData.evidence` after ZK re-verification completes. + evidence: Bytes, } /// Manages the off-chain accusation quorum protocol. @@ -348,12 +358,22 @@ impl AccusationManager { event.accused_address }; - // Cache the failed verification result + // Cache the failed verification result. + // Evidence preimage = `abi.encode(proof.data, public_signals)` — matches + // the on-chain `keccak256(evidence) == dataHash` check in SlashingManager. + let evidence = Bytes::from( + ( + Bytes::copy_from_slice(&event.signed_payload.payload.proof.data), + Bytes::copy_from_slice(&event.signed_payload.payload.proof.public_signals), + ) + .abi_encode(), + ); self.received_data.insert( (accused_address, event.proof_type), ReceivedProofData { data_hash: event.data_hash, verification_passed: false, + evidence, }, ); @@ -387,12 +407,17 @@ impl AccusationManager { ec: &EventContext, ctx: &mut Context, ) { - // Cache as a failed verification for voting on future accusations + // Cache as a failed verification for voting on future accusations. + // `data.evidence` carries the raw `abi.encode(proof.data, public_signals)` + // preimage of `data_hash`, populated by the consistency checker. Slashing + // via this path now binds voter signatures to evidence bytes on-chain + // just like the ProofVerificationFailed path. self.received_data.insert( (data.accused_address, data.proof_type), ReceivedProofData { data_hash: data.data_hash, verification_passed: false, + evidence: data.evidence.clone(), }, ); @@ -592,6 +617,12 @@ impl AccusationManager { } let data_hash = Self::compute_payload_hash(forwarded); + let evidence: Bytes = ( + Bytes::copy_from_slice(&forwarded.payload.proof.data), + Bytes::copy_from_slice(&forwarded.payload.proof.public_signals), + ) + .abi_encode() + .into(); let accused_party_id = accusation.accused_party_id; let forwarded_clone = forwarded.clone(); @@ -634,6 +665,7 @@ impl AccusationManager { data_hash, accused: key.0, proof_type: key.1, + evidence, }, ); @@ -941,6 +973,11 @@ impl AccusationManager { outcome ); + let evidence = self + .received_data + .get(&(pending.accusation.accused, pending.accusation.proof_type)) + .map(|d| d.evidence.clone()) + .unwrap_or_default(); if let Err(err) = self.bus.publish( AccusationQuorumReached { e3_id: self.e3_id.clone(), @@ -950,6 +987,7 @@ impl AccusationManager { votes_for: pending.votes_for, votes_against: pending.votes_against, outcome, + evidence, }, pending.ec, ) { @@ -982,6 +1020,11 @@ impl AccusationManager { outcome ); + let evidence = self + .received_data + .get(&(pending.accusation.accused, pending.accusation.proof_type)) + .map(|d| d.evidence.clone()) + .unwrap_or_default(); if let Err(err) = self.bus.publish( AccusationQuorumReached { e3_id: self.e3_id.clone(), @@ -991,6 +1034,7 @@ impl AccusationManager { votes_for: pending.votes_for, votes_against: pending.votes_against, outcome, + evidence, }, ec.clone(), ) { @@ -1033,12 +1077,14 @@ impl AccusationManager { proof_type: ProofType, data_hash: [u8; 32], passed: bool, + evidence: Bytes, ) { self.received_data.insert( (accused, proof_type), ReceivedProofData { data_hash, verification_passed: passed, + evidence, }, ); } @@ -1093,6 +1139,7 @@ impl AccusationManager { reverif.proof_type, reverif.data_hash, zk_passed, + reverif.evidence.clone(), ); // Get ec from the PendingAccusation @@ -1217,12 +1264,20 @@ impl Handler> for AccusationManager { _ctx: &mut Self::Context, ) -> Self::Result { let (data, _ec) = msg.into_components(); - // Cache successful verification for voting on future accusations + // Cache successful verification for voting on future accusations. + // Evidence preimage = `abi.encode(proof.data, public_signals)`. + let evidence: Bytes = ( + Bytes::copy_from_slice(&data.proof_data), + Bytes::copy_from_slice(&data.public_signals), + ) + .abi_encode() + .into(); self.received_data.insert( (data.address, data.proof_type), ReceivedProofData { data_hash: data.data_hash, verification_passed: true, + evidence, }, ); } diff --git a/crates/zk-prover/src/actors/commitment_consistency_checker.rs b/crates/zk-prover/src/actors/commitment_consistency_checker.rs index 1ce526ca1..872a35a50 100644 --- a/crates/zk-prover/src/actors/commitment_consistency_checker.rs +++ b/crates/zk-prover/src/actors/commitment_consistency_checker.rs @@ -33,6 +33,7 @@ use super::commitment_links::{CommitmentLink, LinkScope}; use actix::{Actor, Addr, Context, Handler}; use alloy::primitives::Address; +use alloy::sol_types::SolValue; use e3_events::{ BusHandle, CommitmentConsistencyCheckComplete, CommitmentConsistencyCheckRequested, CommitmentConsistencyViolation, E3id, EnclaveEvent, EnclaveEventData, EventContext, @@ -50,6 +51,11 @@ struct VerifiedProofData { address: Address, public_signals: ArcBytes, data_hash: [u8; 32], + /// Raw `proof.data` bytes. Together with `public_signals` they form the + /// preimage `abi.encode(proof.data, public_signals)` of `data_hash` — + /// forwarded to slashing so the on-chain contract can verify the dataHash + /// bound in voter signatures. + proof_data: ArcBytes, } /// Describes a source entry whose commitments are inconsistent with a target. @@ -58,6 +64,11 @@ struct Mismatch { address: Address, proof_type: ProofType, data_hash: [u8; 32], + /// Same preimage as `VerifiedProofData.proof_data` paired with + /// `public_signals`. Carried from cache into the emitted violation so + /// downstream slashing can bind voter signatures to evidence bytes. + proof_data: ArcBytes, + public_signals: ArcBytes, } /// Per-E3 actor that enforces cross-circuit commitment consistency. @@ -138,6 +149,8 @@ impl CommitmentConsistencyChecker { address: *addr, proof_type: src_type, data_hash: src.data_hash, + proof_data: src.proof_data.clone(), + public_signals: src.public_signals.clone(), }); break; // one mismatch per source entry is enough } @@ -188,6 +201,8 @@ impl CommitmentConsistencyChecker { address: src.address, proof_type: src_type, data_hash: src.data_hash, + proof_data: src.proof_data.clone(), + public_signals: src.public_signals.clone(), }); } } @@ -235,6 +250,8 @@ impl CommitmentConsistencyChecker { address: src.address, proof_type: src_type, data_hash: src.data_hash, + proof_data: src.proof_data.clone(), + public_signals: src.public_signals.clone(), }); } } @@ -275,26 +292,32 @@ impl CommitmentConsistencyChecker { m.address, m.proof_type, ); - self.emit_violation(m.party_id, m.address, m.proof_type, m.data_hash, ec); + self.emit_violation(&m, ec); } } } /// Publish a [`CommitmentConsistencyViolation`] for the accusation pipeline. - fn emit_violation( - &self, - accused_party_id: u64, - accused_address: Address, - proof_type: ProofType, - data_hash: [u8; 32], - ec: &EventContext, - ) { + fn emit_violation(&self, m: &Mismatch, ec: &EventContext) { + // Evidence preimage = `abi.encode(proof.data, public_signals)`. The + // on-chain `SlashingManager.proposeSlash` recomputes `keccak256(evidence)` + // and requires it to equal each voter's signed `dataHash`. Without + // these bytes, slashing via the consistency-violation path would be + // gated by the evidence binding (safe but unable to slash). + let evidence = alloy::primitives::Bytes::from( + ( + alloy::primitives::Bytes::copy_from_slice(&m.proof_data), + alloy::primitives::Bytes::copy_from_slice(&m.public_signals), + ) + .abi_encode(), + ); let violation = CommitmentConsistencyViolation { e3_id: self.e3_id.clone(), - accused_party_id, - accused_address, - proof_type, - data_hash, + accused_party_id: m.party_id, + accused_address: m.address, + proof_type: m.proof_type, + data_hash: m.data_hash, + evidence, }; if let Err(err) = self.bus.publish(violation, ec.clone()) { error!("Failed to publish CommitmentConsistencyViolation: {err}"); @@ -352,6 +375,7 @@ impl Handler> for CommitmentConsistencyCheck address, public_signals: data.public_signals, data_hash: data.data_hash, + proof_data: data.proof_data, }, ); @@ -373,7 +397,7 @@ impl Handler> for CommitmentCons // Cache each party's proof data for link evaluation. for party in &data.party_proofs { - for (proof_type, public_signals, data_hash) in &party.proofs { + for (proof_type, public_signals, data_hash, proof_data) in &party.proofs { self.insert_verified( party.address, *proof_type, @@ -382,6 +406,7 @@ impl Handler> for CommitmentCons address: party.address, public_signals: public_signals.clone(), data_hash: *data_hash, + proof_data: proof_data.clone(), }, ); } @@ -401,7 +426,7 @@ impl Handler> for CommitmentCons m.address, ); inconsistent_parties.insert(m.party_id); - self.emit_violation(m.party_id, m.address, m.proof_type, m.data_hash, &ec); + self.emit_violation(&m, &ec); } } diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index eaad01f60..c0611ed0c 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -1400,6 +1400,7 @@ impl ProofRequestActor { proof_type: ProofType::C0PkBfv, data_hash, public_signals: proof.public_signals.clone(), + proof_data: proof.data.clone(), }, ec.clone(), ) { diff --git a/crates/zk-prover/src/actors/proof_verification.rs b/crates/zk-prover/src/actors/proof_verification.rs index b93481af2..3b8e71ef4 100644 --- a/crates/zk-prover/src/actors/proof_verification.rs +++ b/crates/zk-prover/src/actors/proof_verification.rs @@ -272,6 +272,7 @@ impl Handler> for ProofVerificationActor { proof_type: ProofType::C0PkBfv, data_hash, public_signals: signed_payload.payload.proof.public_signals.clone(), + proof_data: signed_payload.payload.proof.data.clone(), }, ec, ) { diff --git a/crates/zk-prover/src/actors/share_verification.rs b/crates/zk-prover/src/actors/share_verification.rs index 584582dd1..078802131 100644 --- a/crates/zk-prover/src/actors/share_verification.rs +++ b/crates/zk-prover/src/actors/share_verification.rs @@ -90,6 +90,10 @@ struct PendingVerification { party_proof_hashes: HashMap>, /// Cached (proof_type, public_signals) per party — for commitment consistency checking. party_public_signals: HashMap>, + /// Parallel to `party_public_signals` — raw `proof.data` per (party, proof_type). + /// Needed by `ProofVerificationPassed` so downstream actors can forward + /// evidence bytes to the slashing contract. + party_proof_data: HashMap>, /// BFV preset for circuit artifact resolution. params_preset: e3_fhe_params::BfvPreset, } @@ -120,6 +124,10 @@ struct PendingConsistencyCheck { party_proof_hashes: HashMap>, /// (proof_type, public_signals) per party — for consistency & ZK. party_public_signals: HashMap>, + /// Parallel to `party_public_signals` — raw `proof.data` per (party, proof_type). + /// Needed by `ProofVerificationPassed` so downstream actors can forward + /// evidence bytes to the slashing contract. + party_proof_data: HashMap>, /// Original ECDSA-passed share proofs for ZK dispatch. /// Populated for ShareProofs / ThresholdDecryptionProofs / PkGenerationProofs. ecdsa_passed_share_proofs: Vec, @@ -295,6 +303,7 @@ impl ShareVerificationActor { // Compute proof hashes and public signals for ECDSA-passed parties let mut party_proof_hashes: HashMap> = HashMap::new(); let mut party_public_signals: HashMap> = HashMap::new(); + let mut party_raw_proof_data: HashMap> = HashMap::new(); for party in &ecdsa_passed_parties { let hashes: Vec<(ProofType, [u8; 32])> = party .signed_proofs() @@ -318,8 +327,14 @@ impl ShareVerificationActor { ) }) .collect(); + let datas: Vec<(ProofType, ArcBytes)> = party + .signed_proofs() + .iter() + .map(|signed| (signed.payload.proof_type, signed.payload.proof.data.clone())) + .collect(); party_proof_hashes.insert(party.party_id(), hashes); party_public_signals.insert(party.party_id(), signals); + party_raw_proof_data.insert(party.party_id(), datas); } // Build consistency check request @@ -335,10 +350,15 @@ impl ShareVerificationActor { .get(&party.party_id()) .cloned() .unwrap_or_default(); + let raw_datas = party_raw_proof_data + .get(&party.party_id()) + .cloned() + .unwrap_or_default(); let proofs = signals .into_iter() .zip(hashes) - .map(|((pt, ps), (_, dh))| (pt, ps, dh)) + .zip(raw_datas) + .map(|(((pt, ps), (_, dh)), (_, pd))| (pt, ps, dh, pd)) .collect(); PartyProofData { party_id: party.party_id(), @@ -361,6 +381,7 @@ impl ShareVerificationActor { party_addresses, party_proof_hashes, party_public_signals, + party_proof_data: party_raw_proof_data, ecdsa_passed_share_proofs: Vec::new(), ecdsa_passed_decryption_proofs: Vec::new(), params_preset, @@ -504,6 +525,11 @@ impl ShareVerificationActor { .into_iter() .filter(|(pid, _)| dispatched_party_ids.contains(pid)) .collect(); + let party_proof_data: HashMap> = pending + .party_proof_data + .into_iter() + .filter(|(pid, _)| dispatched_party_ids.contains(pid)) + .collect(); // Store pending ZK verification state. // All prior dishonest parties (pre_dishonest + ECDSA + consistency) are @@ -521,6 +547,7 @@ impl ShareVerificationActor { party_addresses, party_proof_hashes, party_public_signals, + party_proof_data, params_preset: pending.params_preset, }, ); @@ -707,11 +734,16 @@ impl ShareVerificationActor { .copied() .unwrap_or_default(); let signals = pending.party_public_signals.get(&result.sender_party_id); + let datas = pending.party_proof_data.get(&result.sender_party_id); for (i, &(proof_type, data_hash)) in hashes.iter().enumerate() { let public_signals = signals .and_then(|s| s.get(i)) .map(|(_, ps)| ps.clone()) .unwrap_or_default(); + let proof_data = datas + .and_then(|d| d.get(i)) + .map(|(_, pd)| pd.clone()) + .unwrap_or_default(); if let Err(err) = self.bus.publish( ProofVerificationPassed { e3_id: pending.e3_id.clone(), @@ -720,6 +752,7 @@ impl ShareVerificationActor { proof_type, data_hash, public_signals, + proof_data, }, pending.ec.clone(), ) { diff --git a/crates/zk-prover/tests/slashing_integration_tests.rs b/crates/zk-prover/tests/slashing_integration_tests.rs index 9ff14d3c0..b55b21988 100644 --- a/crates/zk-prover/tests/slashing_integration_tests.rs +++ b/crates/zk-prover/tests/slashing_integration_tests.rs @@ -452,11 +452,13 @@ fn sign_vote( /// Encode attestation evidence for `proposeSlash()`. /// -/// Format: `abi.encode(uint256 proofType, address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures)` +/// Format: `abi.encode(uint256 proofType, address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures, bytes evidence)` /// Voters are sorted ascending by address (contract requires strict ascending order). +/// `evidence` is the preimage of `dataHash` (the contract enforces `keccak256(evidence) == commonDataHash`). fn encode_attestation_evidence( proof_type: u8, mut votes: Vec<(Address, bool, FixedBytes<32>, Bytes)>, + evidence: Bytes, ) -> Bytes { votes.sort_by_key(|(addr, _, _, _)| *addr); @@ -473,6 +475,7 @@ fn encode_attestation_evidence( agrees, data_hashes.to_vec(), sigs, + evidence, ) .abi_encode_params() .into() @@ -603,6 +606,7 @@ fn test_evidence_leading_word_is_proof_type() { Bytes::from(vec![0u8; 65]), ), ], + Bytes::new(), ); let leading = U256::from_be_slice(&evidence[..32]); assert_eq!(leading, U256::ZERO, "leading word must be proofType"); @@ -622,10 +626,12 @@ fn test_attestation_evidence_encoding() { .parse() .unwrap(); let proof_type = 0u8; - let data_hash = FixedBytes::from([0xcc; 32]); let accusation_id = compute_accusation_id(chain_id, e3_id, operator, proof_type); + // Evidence preimage must hash to dataHash on chain. + let evidence_bytes = Bytes::from(vec![0xab, 0xcd, 0xef]); + let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); let (voter1, sig1) = sign_vote(&signer1, chain_id, e3_id, accusation_id, true, data_hash); let (voter2, sig2) = sign_vote(&signer2, chain_id, e3_id, accusation_id, true, data_hash); @@ -635,20 +641,22 @@ fn test_attestation_evidence_encoding() { (voter1, true, data_hash, sig1), (voter2, true, data_hash, sig2), ], + evidence_bytes.clone(), ); - // Decode and verify structure: (uint256, address[], bool[], bytes32[], bytes[]) + // Decode and verify structure: (uint256, address[], bool[], bytes32[], bytes[], bytes) type AttestationTuple = ( U256, Vec
, Vec, Vec>, Vec, + Bytes, ); let decoded = AttestationTuple::abi_decode_params(&evidence).expect("evidence should ABI-decode"); - let (dec_proof_type, dec_voters, dec_agrees, dec_hashes, dec_sigs) = decoded; + let (dec_proof_type, dec_voters, dec_agrees, dec_hashes, dec_sigs, dec_evidence) = decoded; assert_eq!(dec_proof_type, U256::from(proof_type), "proofType mismatch"); assert_eq!(dec_voters.len(), 2, "should have 2 voters"); assert!( @@ -658,6 +666,12 @@ fn test_attestation_evidence_encoding() { assert!(dec_agrees.iter().all(|a| *a), "all votes should agree"); assert_eq!(dec_hashes.len(), 2, "should have 2 data hashes"); assert_eq!(dec_sigs.len(), 2, "should have 2 signatures"); + assert_eq!(dec_evidence, evidence_bytes, "evidence bytes mismatch"); + let recomputed: FixedBytes<32> = keccak256(&dec_evidence).into(); + assert_eq!( + recomputed, dec_hashes[0], + "keccak256(evidence) must equal commonDataHash" + ); } // ════════════════════════════════════════════════════════════════════════════ @@ -823,7 +837,8 @@ async fn test_onchain_valid_attestation_executes_slash() { // All 3 voters sign accusation votes (agrees=true) let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let data_hash = FixedBytes::from([0xaa; 32]); + let evidence_bytes = Bytes::from(vec![0xaa]); + let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); let (v1, s1) = sign_vote( &voter_signer1, @@ -857,6 +872,7 @@ async fn test_onchain_valid_attestation_executes_slash() { (v2, true, data_hash, s2), (v3, true, data_hash, s3), ], + evidence_bytes, ); // Verify proposal count before @@ -1001,7 +1017,8 @@ async fn test_onchain_insufficient_attestations_reverts() { data_hash, ); - let evidence = encode_attestation_evidence(proof_type, vec![(v1, true, data_hash, s1)]); + let evidence = + encode_attestation_evidence(proof_type, vec![(v1, true, data_hash, s1)], Bytes::new()); let result = slashing_mgr .proposeSlash(U256::from(e3_id), operator_addr, evidence) @@ -1103,7 +1120,8 @@ async fn test_onchain_voter_not_in_committee_reverts() { // Outsider signs a vote (valid signature, but not a committee member) let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let data_hash = FixedBytes::from([0xcc; 32]); + let evidence_bytes = Bytes::from(vec![0xcc]); + let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); let (v_out, s_out) = sign_vote( &outsider_signer, @@ -1114,7 +1132,11 @@ async fn test_onchain_voter_not_in_committee_reverts() { data_hash, ); - let evidence = encode_attestation_evidence(proof_type, vec![(v_out, true, data_hash, s_out)]); + let evidence = encode_attestation_evidence( + proof_type, + vec![(v_out, true, data_hash, s_out)], + evidence_bytes, + ); let result = slashing_mgr .proposeSlash(U256::from(e3_id), operator_addr, evidence) @@ -1216,7 +1238,8 @@ async fn test_onchain_invalid_vote_signature_reverts() { // Impersonator signs the vote with their key, but we claim it's from victim_signer let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let data_hash = FixedBytes::from([0xdd; 32]); + let evidence_bytes = Bytes::from(vec![0xdd]); + let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); // Sign using impersonator's key but construct the digest for victim_signer's address let digest = compute_vote_digest( @@ -1232,12 +1255,13 @@ async fn test_onchain_invalid_vote_signature_reverts() { .expect("signing should succeed"); // Build evidence claiming the vote is from victim_signer but signed by impersonator - let evidence = ( + let evidence: Bytes = ( U256::from(proof_type), vec![victim_signer.address()], vec![true], vec![data_hash], vec![Bytes::from(bad_sig.as_bytes().to_vec())], + evidence_bytes, ) .abi_encode_params() .into(); @@ -1343,7 +1367,8 @@ async fn test_onchain_duplicate_voter_reverts() { // Create TWO votes from the same voter (duplicate addresses) let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let data_hash = FixedBytes::from([0xee; 32]); + let evidence_bytes = Bytes::from(vec![0xee]); + let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); let (voter, sig) = sign_vote( &voter_signer, @@ -1356,12 +1381,13 @@ async fn test_onchain_duplicate_voter_reverts() { // Submit evidence with duplicate voter entries (bypassing encode_attestation_evidence // which would deduplicate — construct manually to have same address appear twice) - let evidence = ( + let evidence: Bytes = ( U256::from(proof_type), vec![voter, voter], vec![true, true], vec![data_hash, data_hash], vec![sig.clone(), sig], + evidence_bytes, ) .abi_encode_params() .into(); @@ -1468,7 +1494,8 @@ async fn test_onchain_duplicate_evidence_reverts() { .unwrap(); let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let data_hash = FixedBytes::from([0xff; 32]); + let evidence_bytes = Bytes::from(vec![0xff]); + let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); let (v1, s1) = sign_vote( &voter_signer1, @@ -1490,6 +1517,7 @@ async fn test_onchain_duplicate_evidence_reverts() { let evidence = encode_attestation_evidence( proof_type, vec![(v1, true, data_hash, s1), (v2, true, data_hash, s2)], + evidence_bytes, ); // First submission should succeed diff --git a/docs/pages/cryptography.mdx b/docs/pages/cryptography.mdx index 4ad3006f6..6d4fb5ce7 100644 --- a/docs/pages/cryptography.mdx +++ b/docs/pages/cryptography.mdx @@ -235,21 +235,49 @@ Each node's fold attestation signs an EIP-712-style digest over `(chainId, e3Id, partyId, skAggCommit, esmAggCommit)` with the operator's registered key. The on-chain [`DkgFoldAttestationVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol) -then enforces three properties per attestation: - -1. **Signer = registered operator**: `ecrecover` over the signed digest must equal the `node` - address bound to that `partyId` in the bundle, and that address must be an active committee - member for this E3 (`isCommitteeMemberActive(e3Id, node)`). -2. **Commitments match the DKG proof**: the signed `skAggCommit` and `esmAggCommit` must equal the +then enforces four properties per attestation: + +1. **Slot binding**: `getCommitteeNodeAt(e3Id, binding.partyId) == binding.node`. The aggregator + cannot claim that the operator at `topNodes[0]` is party 1 — the registry's canonical slot + mapping is checked directly. Available before `publishCommittee` completes (the registry sorts + `topNodes` at finalisation, well before the attestation verifier runs). +2. **Signer = registered operator**: `ecrecover` over the signed digest must equal that `node` + address, and the address must be an active committee member for this E3 + (`isCommitteeMemberActive(e3Id, node)`). +3. **Commitments match the DKG proof**: the signed `skAggCommit` and `esmAggCommit` must equal the `partyId`'s slot in the DKG aggregator proof's public inputs. -3. **Honest-set cardinality**: the bundle must contain exactly one attestation per honest party in +4. **Honest-set cardinality**: the bundle must contain exactly one attestation per honest party in the proof, with `partyId` strictly ascending (no duplicates, no partial subsets). This closes the per-operator attribution gap: the aggregator cannot present a valid DKG proof without also producing one signed attestation per honest party, each bound to the operator -registered at the corresponding `topNodes` slot. The recovered `partyIds`, `skAggCommits`, and -`esmAggCommits` are persisted as on-chain anchors (`dkgPartyIds`, `dkgSkAggCommits`, -`dkgEsmAggCommits`) so future decryption proofs can be checked against them. +registered at the corresponding `topNodes` slot, and slot-shuffling between operators is +structurally prevented. The recovered `partyIds`, `skAggCommits`, and `esmAggCommits` are persisted +as on-chain anchors (`dkgPartyIds`, `dkgSkAggCommits`, `dkgEsmAggCommits`) so future decryption +proofs can be checked against them. + +#### Per-operator attribution for slashing + +When a node misbehaves, the off-chain accusation quorum protocol produces a +[`SlashingManager.proposeSlash`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/slashing/SlashingManager.sol) +call. The on-chain check enforces, in `_verifyAttestationEvidence`: + +1. **Voter quorum and signatures**: ≥ `M` committee-member voters (sorted ascending, accused + excluded), each signing `(VOTE_TYPEHASH, chainId, e3Id, accusationId, voter, agrees, dataHash)` + over the same accusation. +2. **Same evidence across voters**: all `dataHashes[i]` must equal a common `dataHash`. Without this + each voter could be agreeing to "something bad" of their own — the quorum would not actually + agree on what they observed. +3. **Evidence preimage on chain**: the proposal payload includes a `bytes evidence` field, and + `keccak256(evidence) == commonDataHash` is required. The evidence is the raw + `abi.encode(proof.data, public_signals)` of the faulty proof, so the contract — and any third + party reading the transaction calldata — can audit exactly which bytes the committee voted on, + not just a hash. + +Combined with the fold-attestation chain, the slashing flow forms a complete attribution chain: +operator → registered address at `topNodes[partyId]` → signature on a specific commitment → faulty +proof bytes auditable on chain. Both the DKG fault-attribution side and the slashing-evidence side +are cryptographically bound on chain. #### Committee hash binding diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index 9b6adc25b..ec3a3ca89 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -386,11 +386,14 @@ the registry runs two checks in order: 2. **Fold-attestation verification** via [`DkgFoldAttestationVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol). It walks the bundle's `(partyId, node)` bindings — strictly ascending by `partyId`, no duplicates - — and for each binding: requires `isCommitteeMemberActive(e3Id, node)`, recovers the signer of - the attestation via `ecrecover` against the EIP-191/712 digest, requires `signer == node`, and - checks that the signed `skAggCommit` / `esmAggCommit` match the DKG proof's surfaced commitments - at that party's slot. The honest-set cardinality must equal the honest-party count derived from - the proof's public-input shape (`(len - 6) / 3`). + — and for each binding: requires `getCommitteeNodeAt(e3Id, partyId) == node` (slot binding), + requires `isCommitteeMemberActive(e3Id, node)`, recovers the signer of the attestation via + `ecrecover` against the EIP-191/712 digest, requires `signer == node`, and checks that the signed + `skAggCommit` / `esmAggCommit` match the DKG proof's surfaced commitments at that party's slot. + The honest-set cardinality must equal the honest-party count derived from the proof's + public-input shape (`(len - 6) / 3`). The slot binding prevents an aggregator from reassigning a + node's signed attestation to a different slot — even an actively cooperating signer cannot be + "moved" between partyIds because the registry's own slot mapping is checked structurally. Only if both succeed does the registry store the per-party anchors: @@ -415,6 +418,27 @@ compares the surfaced commitments against the anchors stored at `publishCommitte differs the proof is rejected — and the plaintext is decoded from the proof's last 100 public inputs (one `uint64` coefficient each, packed into bytes). +### Slashing-evidence binding + +When fault attribution leads to a slash, the off-chain accusation quorum produces a payload that +[`SlashingManager.proposeSlash`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/slashing/SlashingManager.sol) +decodes as `(proofType, voters[], agrees[], dataHashes[], signatures[], evidence)`. +`_verifyAttestationEvidence` enforces: + +- ≥ `M` voters, sorted ascending, accused excluded, each signing the standard + `VOTE_TYPEHASH(chainId, e3Id, accusationId, voter, agrees, dataHash)` digest. +- All `dataHashes[i]` equal a common `dataHash` (voters must agree on the same evidence, not just + "something bad"). +- `keccak256(evidence) == commonDataHash`. The `evidence` field is the raw + `abi.encode(proof.data, public_signals)` of the faulty proof, transported on chain so the contract + — and any third party — can recompute the dataHash from the bytes the committee voted on. + +On the Rust side, the evidence bytes are populated for every accusation path that leads to a slash +(ZK verification failures via `ProofVerificationFailed`, forwarded C3a/C3b accusations via the +re-verification path, and commitment-consistency violations via `CommitmentConsistencyViolation`). +This means every slash carries auditable evidence to chain — there is no path that produces a slash +without binding voter signatures to specific proof bytes. + --- ## Proof Lifecycle diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index cadedd249..8621d44a4 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -2564,5 +2564,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" + "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 432544d0d..5496e9275 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -1005,5 +1005,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" + "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 558b2202c..ef2a767b2 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -161,6 +161,22 @@ "name": "PartyIdNotInProof", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "committeeSize", + "type": "uint256" + } + ], + "name": "PartyIdOutOfBounds", + "type": "error" + }, { "inputs": [], "name": "PkCommitmentRequired", @@ -675,6 +691,30 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + } + ], + "name": "getCommitteeNodeAt", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1059,5 +1099,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" + "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index 6578b63b7..deda36500 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2097,5 +2097,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" + "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index 3a73fe649..646225a73 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -1006,5 +1006,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" + "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index 07d9c8eaa..ad0651ef8 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -198,6 +198,22 @@ "name": "PartyIdNotInProof", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "committeeSize", + "type": "uint256" + } + ], + "name": "PartyIdOutOfBounds", + "type": "error" + }, { "inputs": [], "name": "PkCommitmentRequired", @@ -878,6 +894,30 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + } + ], + "name": "getCommitteeNodeAt", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1397,30 +1437,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b614548806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061026b575f3560e01c80639a7a2ffc1161014b578063dbb06c93116100bf578063ebf0c71711610084578063ebf0c717146105f3578063f1650536146105fb578063f2fde38b14610615578063f379b0df14610628578063f52fd80314610662578063f6fc05d5146106d2575f5ffd5b8063dbb06c9314610595578063e4be6e3d146105a7578063e59e4695146105ba578063e6745e13146105cd578063e82f3b70146105e0575f5ffd5b8063bff232c111610110578063bff232c11461050d578063c2b40ae414610520578063c3a0ec301461053f578063ca2869a014610550578063cd6dc6871461056f578063da881e5a14610582575f5ffd5b80639a7a2ffc146104735780639f0f874a146104af578063a0164930146104b8578063a8a4d69b146104d8578063b8ab4704146104eb575f5ffd5b80635d504776116101e25780638a78bb15116101a75780638a78bb15146103fd5780638cb89ecb146104105780638d1ddfb11461042f5780638da5cb5b146104455780638e5ce3ad1461044d5780639015d37114610460575f5ffd5b80635d5047761461038257806370e36bbe14610395578063715018a6146103a85780637c92f524146103b057806385814243146103dd575f5ffd5b80632800d829116102335780632800d829146102f1578063291a691b146103045780632e7b716d146103275780634d6861a61461033a57806350e6d94c1461034d5780635302670f1461036f575f5ffd5b8063096b810a1461026f578063099a161a146102845780630bbfade7146102aa5780630f3e3412146102bd57806317d61120146102d0575b5f5ffd5b61028261027d3660046138e0565b6106db565b005b6102976102923660046138fb565b610827565b6040519081526020015b60405180910390f35b6102826102b8366004613956565b610860565b6102826102cb3660046138fb565b610aa4565b6102e36102de3660046138fb565b610ae7565b6040516102a1929190613a77565b6102976102ff3660046138fb565b610c90565b610317610312366004613aa4565b610cdc565b60405190151581526020016102a1565b6103176103353660046138e0565b610eb6565b6103176103483660046138fb565b610f69565b61031761035b3660046138e0565b60066020525f908152604090205460ff1681565b61028261037d3660046138e0565b610fa8565b610317610390366004613add565b610ff9565b6102826103a33660046138e0565b61103c565b6102826110b2565b6103c36103be366004613b0b565b6110c5565b6040805192835263ffffffff9091166020830152016102a1565b6001546103f0906001600160a01b031681565b6040516102a19190613b40565b61028261040b3660046138e0565b61126c565b61029761041e3660046138fb565b60096020525f908152604090205481565b600454600160281b900464ffffffffff16610297565b6103f06113aa565b600b546103f0906001600160a01b031681565b61031761046e3660046138e0565b6113d8565b6104996104813660046138e0565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102a1565b61029760035481565b6104cb6104c63660046138fb565b6113f5565b6040516102a19190613b54565b6103176104e6366004613add565b61148b565b6104fe6104f93660046138fb565b6114ce565b6040516102a193929190613b66565b61028261051b3660046138e0565b611619565b61029761052e3660046138fb565b60086020525f908152604090205481565b6001546001600160a01b03166103f0565b61029761055e3660046138fb565b5f9081526008602052604090205490565b61028261057d366004613ba8565b611691565b6103176105903660046138fb565b6117ee565b5f546103f0906001600160a01b031681565b600c546103f0906001600160a01b031681565b6102826105c83660046138e0565b611ad1565b6102826105db366004613bd2565b611b49565b6102976105ee3660046138fb565b611d0c565b610297611d3d565b610603601481565b60405160ff90911681526020016102a1565b6102826106233660046138e0565b611d4f565b6004546106449064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102a1565b6106a36106703660046138fb565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102a1949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61029760025481565b6106e36113aa565b6001600160a01b0316336001600160a01b0316148061070c57506001546001600160a01b031633145b61072957604051632864c4e160e01b815260040160405180910390fd5b610732816113d8565b819061075b576040516381e5828960e01b81526004016107529190613b40565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107899060049083611d89565b6001600160a01b0382165f908152600660205260408120805460ff1916905560028054916107b683613c06565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a602052604081206004810154610856576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b5f888152600a602052604090206002815460ff16600381111561088557610885613c1b565b146108a357604051634f4b461f60e11b815260040160405180910390fd5b6004810154156108c65760405163632a22bb60e01b815260040160405180910390fd5b856108e457604051636caad1ed60e11b815260040160405180910390fd5b5f6109488260060180548060200260200160405190810160405280929190818152602001828054801561093e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610920575b505050505061202b565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa15801561099a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109c19190810190613dab565b9050806101c00151156109de576109de8b828a858b8b8b8b612131565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610a3c575f5ffd5b505af1158015610a4e573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610a8f96959493929190613f6d565b60405180910390a25050505050505050505050565b610aac61232d565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610b1d57610b1d613c2f565b604051908082528060200260200182016040528015610b46578160200160208202803683370190505b509450806001600160401b03811115610b6157610b61613c2f565b604051908082528060200260200182016040528015610b8a578160200160208202803683370190505b5093505f805b83811015610c86575f856006018281548110610bae57610bae613fba565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610bf457610bf4613c1b565b03610c7d5780888481518110610c0c57610c0c613fba565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610c6457610c64613fba565b602090810291909101015282610c7981613fce565b9350505b50600101610b90565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610cb457610cb4613c1b565b03610cd257604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d075760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d2b57610d2b613c1b565b14610d49576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610d90573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db49190613fe6565b905080610dc76040860160208701614010565b63ffffffff161115610ddf6040860160208701614010565b829091610e0d576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610752565b5050815460ff1916600190811783558201859055436002830155600354610e349042614029565b6003830155610e48600583018560026137de565b50610e51611d3d565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ea2928a928a929161403c565b60405180910390a250600195945050505050565b5f610ec0826113d8565b610ecb57505f919050565b6001546001600160a01b0316610ef4576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f24908590600401613b40565b602060405180830381865afa158015610f3f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f63919061408c565b92915050565b5f818152600a602052604081206001815460ff166003811115610f8e57610f8e613c1b565b14610f9b57505f92915050565b6003015442111592915050565b610fb061232d565b6001600160a01b038116610fd75760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561103457611034613c1b565b149392505050565b61104461232d565b6001600160a01b03811661106b5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6110ba61232d565b6110c35f61235f565b565b600b545f9081906001600160a01b031633146110f45760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561111957611119613c1b565b1461113757604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561117757611177613c1b565b1461118757600b01549150611264565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916111bb83613c06565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161120c929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6112746113aa565b6001600160a01b0316336001600160a01b0316148061129d57506001546001600160a01b031633145b6112ba57604051632864c4e160e01b815260040160405180910390fd5b6112c3816113d8565b6113a75760048054600160281b900464ffffffffff16906112ed906001600160a01b0384166123cf565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161133e83613fce565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161081b565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611428576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561147e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611460575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156114c5576114c5613c1b565b14159392505050565b5f8181526009602052604090205460609081908190611500576040516322e679e360e11b815260040160405180910390fd5b5f848152600d60209081526040808320600e8352818420600f8452938290208154835181860281018601909452808452919493909291859183018282801561156557602002820191905f5260205f20905b815481526020019060010190808311611551575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156115b557602002820191905f5260205f20905b8154815260200190600101908083116115a1575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561160557602002820191905f5260205f20905b8154815260200190600101908083116115f1575b505050505090509250925092509193909250565b61162161232d565b6001600160a01b0381166116485760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f61169a6125a5565b805490915060ff600160401b82041615906001600160401b03165f811580156116c05750825b90505f826001600160401b031660011480156116db5750303b155b9050811580156116e9575080155b156117075760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561173157845460ff60401b1916600160401b1785555b6001600160a01b0387166117585760405163d92e233d60e01b815260040160405180910390fd5b611761336125cd565b61176d600460146125de565b61177686610aa4565b61177e6113aa565b6001600160a01b0316876001600160a01b03161461179f5761179f87611d4f565b83156117e557845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561181257611812613c1b565b0361183057604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561184857611848613c1b565b1461186657604051631860f69960e31b815260040160405180910390fd5b8060030154421161188a57604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061196f578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611950575f5ffd5b505af1158015611962573d5f5f3e3d5ffd5b505f979650505050505050565b6119788261265d565b815460ff191660021782556006820154600b83018190555f816001600160401b038111156119a8576119a8613c2f565b6040519080825280602002602001820160405280156119d1578160200160208202803683370190505b5090505f5b82811015611a4357846009015f8660060183815481106119f8576119f8613fba565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611a3057611a30613fba565b60209081029190910101526001016119d6565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611a86575f5ffd5b505af1158015611a98573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ea29291906140a5565b611ad961232d565b6001600160a01b038116611b005760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611b6d57611b6d613c1b565b03611b8b57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611ba357611ba3613c1b565b14611bc157604051631860f69960e31b815260040160405180910390fd5b8060030154421115611be657604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611c185760405163257309f160e11b815260040160405180910390fd5b611c2133610eb6565b611c3e5760405163149fbcfd60e11b815260040160405180910390fd5b611c49338385612783565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611cc890839083612954565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611d38576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611d4a60046014612b55565b905090565b611d5761232d565b6001600160a01b038116611d80575f604051631e4fbdf760e01b81526004016107529190613b40565b6113a78161235f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611dc85760405162461bcd60e51b8152600401610752906140b7565b825464ffffffffff600160281b90910481169082168111611e265760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610752565b825f5b81866001015f611e398488612c4e565b64ffffffffff1681526020019081526020015f20819055505f816001611e5f9190614101565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611e945750612023565b600185165f03611f5b575f611eb383611eae88600161411a565b612c4e565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f1491600401614137565b602060405180830381865af4158015611f2f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f539190613fe6565b93505061200f565b5f611f6b83611eae600189614167565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611fcc91600401614137565b602060405180830381865af4158015611fe7573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061200b9190613fe6565b9350505b50647fffffffff600194851c169301611e29565b505050505050565b80515f908161203b826014614184565b6001600160401b0381111561205257612052613c2f565b6040519080825280601f01601f19166020018201604052801561207c576020820181803683370190505b5090505f5b82811015612121575f85828151811061209c5761209c613fba565b602002602001015160601b90505f8260146120b79190614184565b90505f5b6014811015612113578281601481106120d6576120d6613fba565b1a60f81b856120e58385614029565b815181106120f5576120f5613fba565b60200101906001600160f81b03191690815f1a9053506001016120bb565b505050806001019050612081565b5080516020909101209392505050565b8261214f57604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b8152600401612186949392919061419b565b602060405180830381865afa1580156121a1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121c5919061408c565b6121e25760405163051d8aa760e51b815260040160405180910390fd5b8061220057604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661222957604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b815260040161227897969594939291906141ba565b5f60405180830381865afa158015612292573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526122b99190810190614294565b5f8e8152600d6020908152604090912084519497509295509093506122e1929086019061387f565b505f8b8152600e6020908152604090912083516123009285019061387f565b505f8b8152600f60209081526040909120825161231f9284019061387f565b505050505050505050505050565b336123366113aa565b6001600160a01b0316146110c3573360405163118cdaa760e01b81526004016107529190613b40565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061241e5760405162461bcd60e51b8152600401610752906140b7565b825464ffffffffff908116908216106124715760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610752565b61247c81600161411a565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6124b38487612c4e565b64ffffffffff16815260208101919091526040015f2055600183161561259e575f6124e382611eae600187614167565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161254491600401614137565b602060405180830381865af415801561255f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125839190613fe6565b647fffffffff600195861c16949093509190910190506124a3565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f63565b6125d5612c6b565b6113a781612c90565b602060ff8216111561262c5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610752565b61263d600160ff831681901b614378565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b8181101561277e575f612678826001614029565b90505b82811015612775575f84600601838154811061269957612699613fba565b5f9182526020822001546006870180546001600160a01b03909216935090849081106126c7576126c7613fba565b5f918252602090912001546001600160a01b039081169150821681101561276b57808660060185815481106126fe576126fe613fba565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055508186600601848154811061273f5761273f613fba565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b505060010161267b565b50600101612664565b505050565b5f82116127a35760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166127cc576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161280291614378565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612849573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061286d9190613fe6565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128c0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128e49190613fe6565b90505f81116129065760405163aeaddff160e01b815260040160405180910390fd5b5f612911828461438b565b90505f81116129335760405163149fbcfd60e11b815260040160405180910390fd5b808611156117e55760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156129d257508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612b4e565b5f5f90505f876009015f855f815481106129ee576129ee613fba565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612a76575f896009015f878481548110612a3857612a38613fba565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612a6d578092508193505b50600101612a17565b50808610612a8a575f945050505050612b4e565b5f88600a015f868581548110612aa257612aa2613fba565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612adf57612adf613c1b565b021790555086848381548110612af757612af7613fba565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612ba85760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610752565b602060ff83161115612bcc5760405162461bcd60e51b8152600401610752906143aa565b8254600160281b900464ffffffffff1680612beb60ff851660026144fb565b64ffffffffff161015612c3b5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610752565b612c46848285612c98565b949350505050565b5f81612c6160ff851663ffffffff614514565b612b4e919061411a565b612c73612d60565b6110c357604051631afcd79f60e31b815260040160405180910390fd5b611d57612c6b565b5f602060ff83161115612cbd5760405162461bcd60e51b8152600401610752906143aa565b8264ffffffffff165f03612cdb57612cd482612d79565b9050612b4e565b5f612ce7836001614101565b60ff166001600160401b03811115612d0157612d01613c2f565b604051908082528060200260200182016040528015612d2a578160200160208202803683370190505b509050612d3985858584613413565b808360ff1681518110612d4e57612d4e613fba565b60200260200101519150509392505050565b5f612d696125a5565b54600160401b900460ff16919050565b5f8160ff165f03612d8b57505f919050565b8160ff16600103612dbd57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff16600203612def57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff16600303612e2157507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff16600403612e5357507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff16600503612e8557507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff16600603612eb757507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff16600703612ee957507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612f1b57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612f4d57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612f7f57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612fb157507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612fe357507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361301557507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361304757507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361307957507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036130ab57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff166011036130dd57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361310f57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361314157507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361317357507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036131a557507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff166016036131d757507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361320957507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361323b57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361326d57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361329f57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b036132d157507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361330357507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361333557507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361336757507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361339957507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036133cb57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610752565b602060ff831611156134375760405162461bcd60e51b8152600401610752906143aa565b5f8364ffffffffff161161349b5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610752565b5f6134a7600185614167565b9050600181165f036134fa57846001015f6134c25f84612c4e565b64ffffffffff1681526020019081526020015f2054825f815181106134e9576134e9613fba565b602002602001018181525050613522565b6135035f612d79565b825f8151811061351557613515613fba565b6020026020010181815250505b5f5b8360ff168160ff16101561202357600182165f0361361a5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061357657613576613fba565b6020026020010151815260200161358c85612d79565b8152506040518263ffffffff1660e01b81526004016135ab9190614137565b602060405180830381865af41580156135c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135ea9190613fe6565b836135f6836001614101565b60ff168151811061360957613609613fba565b6020026020010181815250506137cb565b5f613626826001614101565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156136c8575f876001015f61367d85600161366c9190614101565b60018864ffffffffff16901c612c4e565b64ffffffffff1681526020019081526020015f2054905080858460016136a39190614101565b60ff16815181106136b6576136b6613fba565b602002602001018181525050506137c9565b5f876001015f6136df85600188611eae9190614167565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061373657613736613fba565b60200260200101518152506040518263ffffffff1660e01b815260040161375d9190614137565b602060405180830381865af4158015613778573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061379c9190613fe6565b856137a8856001614101565b60ff16815181106137bb576137bb613fba565b602002602001018181525050505b505b647fffffffff600192831c169101613524565b60018301918390821561386f579160200282015f5b8382111561383d57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026137f3565b801561386d5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261383d565b505b5061387b9291506138b8565b5090565b828054828255905f5260205f2090810192821561386f579160200282015b8281111561386f57825182559160200191906001019061389d565b5b8082111561387b575f81556001016138b9565b6001600160a01b03811681146113a7575f5ffd5b5f602082840312156138f0575f5ffd5b8135612b4e816138cc565b5f6020828403121561390b575f5ffd5b5035919050565b5f5f83601f840112613922575f5ffd5b5081356001600160401b03811115613938575f5ffd5b60208301915083602082850101111561394f575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b03121561396d575f5ffd5b8835975060208901356001600160401b03811115613989575f5ffd5b6139958b828c01613912565b9098509650506040890135945060608901356001600160401b038111156139ba575f5ffd5b6139c68b828c01613912565b90955093505060808901356001600160401b038111156139e4575f5ffd5b6139f08b828c01613912565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613a3d5781516001600160a01b0316865260209586019590910190600101613a16565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613a3d578151865260209586019590910190600101613a59565b604081525f613a896040830185613a04565b8281036020840152613a9b8185613a47565b95945050505050565b5f5f5f60808486031215613ab6575f5ffd5b833592506020840135915060808401851015613ad0575f5ffd5b6040840190509250925092565b5f5f60408385031215613aee575f5ffd5b823591506020830135613b00816138cc565b809150509250929050565b5f5f5f60608486031215613b1d575f5ffd5b833592506020840135613b2f816138cc565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612b4e6020830184613a04565b606081525f613b786060830186613a47565b8281036020840152613b8a8186613a47565b90508281036040840152613b9e8185613a47565b9695505050505050565b5f5f60408385031215613bb9575f5ffd5b8235613bc4816138cc565b946020939093013593505050565b5f5f60408385031215613be3575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613c1457613c14613bf2565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613c6657613c66613c2f565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613c9457613c94613c2f565b604052919050565b805160048110611d38575f5ffd5b5f82601f830112613cb9575f5ffd5b604080519081016001600160401b0381118282101715613cdb57613cdb613c2f565b8060405250806040840185811115613cf1575f5ffd5b845b81811015613d0b578051835260209283019201613cf3565b509195945050505050565b8051611d38816138cc565b805160ff81168114611d38575f5ffd5b5f82601f830112613d40575f5ffd5b81516001600160401b03811115613d5957613d59613c2f565b613d6c601f8201601f1916602001613c6c565b818152846020838601011115613d80575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611d38575f5ffd5b5f60208284031215613dbb575f5ffd5b81516001600160401b03811115613dd0575f5ffd5b82016102008185031215613de2575f5ffd5b613dea613c43565b81518152613dfa60208301613c9c565b602082015260408281015190820152613e168560608401613caa565b606082015260a08201516080820152613e3160c08301613d16565b60a0820152613e4260e08301613d21565b60c08201526101008201516001600160401b03811115613e60575f5ffd5b613e6c86828501613d31565b60e083015250613e7f6101208301613d16565b610100820152613e926101408301613d16565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613ec8575f5ffd5b613ed486828501613d31565b61018083015250613ee86101c08301613d16565b6101a0820152613efb6101e08301613d9c565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613a3d5781546001600160a01b0316865260209095019460019182019101613f1e565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f613f7f6080830189613f09565b8281036020840152613f9281888a613f45565b90508560408401528281036060840152613fad818587613f45565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60018201613fdf57613fdf613bf2565b5060010190565b5f60208284031215613ff6575f5ffd5b5051919050565b803563ffffffff81168114611d38575f5ffd5b5f60208284031215614020575f5ffd5b612b4e82613ffd565b80820180821115610f6357610f63613bf2565b84815260a0810160208201855f5b60028110156140775763ffffffff61406183613ffd565b168352602092830192919091019060010161404a565b50505060608201939093526080015292915050565b5f6020828403121561409c575f5ffd5b612b4e82613d9c565b604081525f613a896040830185613f09565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f6357610f63613bf2565b64ffffffffff8181168382160190811115610f6357610f63613bf2565b6040810181835f5b600281101561415e57815183526020928301929091019060010161413f565b50505092915050565b64ffffffffff8281168282160390811115610f6357610f63613bf2565b8082028115828204841417610f6357610f63613bf2565b848152836020820152606060408201525f613b9e606083018486613f45565b60018060a01b038816815286602082015285604082015260a060608201525f6141e760a083018688613f45565b82810360808401526141fa818587613f45565b9a9950505050505050505050565b5f6001600160401b0382111561422057614220613c2f565b5060051b60200190565b5f82601f830112614239575f5ffd5b815161424c61424782614208565b613c6c565b8082825260208201915060208360051b86010192508583111561426d575f5ffd5b602085015b8381101561428a578051835260209283019201614272565b5095945050505050565b5f5f5f606084860312156142a6575f5ffd5b83516001600160401b038111156142bb575f5ffd5b8401601f810186136142cb575f5ffd5b80516142d961424782614208565b8082825260208201915060208360051b8501019250888311156142fa575f5ffd5b6020840193505b8284101561431c578351825260209384019390910190614301565b8096505050505060208401516001600160401b0381111561433b575f5ffd5b6143478682870161422a565b92505060408401516001600160401b03811115614362575f5ffd5b61436e8682870161422a565b9150509250925092565b81810381811115610f6357610f63613bf2565b5f826143a557634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156112645780850481111561440c5761440c613bf2565b600184161561441a57908102905b60019390931c9280026143f1565b5f8261443657506001610f63565b8161444257505f610f63565b8160018114614458576002811461446257614494565b6001915050610f63565b60ff84111561447357614473613bf2565b6001841b915064ffffffffff82111561448e5761448e613bf2565b50610f63565b5060208310610133831016604e8410600b84101617156144cc575081810a64ffffffffff8111156144c7576144c7613bf2565b610f63565b6144dc64ffffffffff84846143ed565b8064ffffffffff048211156144f3576144f3613bf2565b029392505050565b5f612b4e64ffffffffff841664ffffffffff8416614428565b64ffffffffff818116838216029081169081811461453457614534613bf2565b509291505056fea164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061026b575f3560e01c80639a7a2ffc1161014b578063dbb06c93116100bf578063ebf0c71711610084578063ebf0c717146105f3578063f1650536146105fb578063f2fde38b14610615578063f379b0df14610628578063f52fd80314610662578063f6fc05d5146106d2575f5ffd5b8063dbb06c9314610595578063e4be6e3d146105a7578063e59e4695146105ba578063e6745e13146105cd578063e82f3b70146105e0575f5ffd5b8063bff232c111610110578063bff232c11461050d578063c2b40ae414610520578063c3a0ec301461053f578063ca2869a014610550578063cd6dc6871461056f578063da881e5a14610582575f5ffd5b80639a7a2ffc146104735780639f0f874a146104af578063a0164930146104b8578063a8a4d69b146104d8578063b8ab4704146104eb575f5ffd5b80635d504776116101e25780638a78bb15116101a75780638a78bb15146103fd5780638cb89ecb146104105780638d1ddfb11461042f5780638da5cb5b146104455780638e5ce3ad1461044d5780639015d37114610460575f5ffd5b80635d5047761461038257806370e36bbe14610395578063715018a6146103a85780637c92f524146103b057806385814243146103dd575f5ffd5b80632800d829116102335780632800d829146102f1578063291a691b146103045780632e7b716d146103275780634d6861a61461033a57806350e6d94c1461034d5780635302670f1461036f575f5ffd5b8063096b810a1461026f578063099a161a146102845780630bbfade7146102aa5780630f3e3412146102bd57806317d61120146102d0575b5f5ffd5b61028261027d3660046138e0565b6106db565b005b6102976102923660046138fb565b610827565b6040519081526020015b60405180910390f35b6102826102b8366004613956565b610860565b6102826102cb3660046138fb565b610aa4565b6102e36102de3660046138fb565b610ae7565b6040516102a1929190613a77565b6102976102ff3660046138fb565b610c90565b610317610312366004613aa4565b610cdc565b60405190151581526020016102a1565b6103176103353660046138e0565b610eb6565b6103176103483660046138fb565b610f69565b61031761035b3660046138e0565b60066020525f908152604090205460ff1681565b61028261037d3660046138e0565b610fa8565b610317610390366004613add565b610ff9565b6102826103a33660046138e0565b61103c565b6102826110b2565b6103c36103be366004613b0b565b6110c5565b6040805192835263ffffffff9091166020830152016102a1565b6001546103f0906001600160a01b031681565b6040516102a19190613b40565b61028261040b3660046138e0565b61126c565b61029761041e3660046138fb565b60096020525f908152604090205481565b600454600160281b900464ffffffffff16610297565b6103f06113aa565b600b546103f0906001600160a01b031681565b61031761046e3660046138e0565b6113d8565b6104996104813660046138e0565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102a1565b61029760035481565b6104cb6104c63660046138fb565b6113f5565b6040516102a19190613b54565b6103176104e6366004613add565b61148b565b6104fe6104f93660046138fb565b6114ce565b6040516102a193929190613b66565b61028261051b3660046138e0565b611619565b61029761052e3660046138fb565b60086020525f908152604090205481565b6001546001600160a01b03166103f0565b61029761055e3660046138fb565b5f9081526008602052604090205490565b61028261057d366004613ba8565b611691565b6103176105903660046138fb565b6117ee565b5f546103f0906001600160a01b031681565b600c546103f0906001600160a01b031681565b6102826105c83660046138e0565b611ad1565b6102826105db366004613bd2565b611b49565b6102976105ee3660046138fb565b611d0c565b610297611d3d565b610603601481565b60405160ff90911681526020016102a1565b6102826106233660046138e0565b611d4f565b6004546106449064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102a1565b6106a36106703660046138fb565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102a1949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61029760025481565b6106e36113aa565b6001600160a01b0316336001600160a01b0316148061070c57506001546001600160a01b031633145b61072957604051632864c4e160e01b815260040160405180910390fd5b610732816113d8565b819061075b576040516381e5828960e01b81526004016107529190613b40565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107899060049083611d89565b6001600160a01b0382165f908152600660205260408120805460ff1916905560028054916107b683613c06565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a602052604081206004810154610856576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b5f888152600a602052604090206002815460ff16600381111561088557610885613c1b565b146108a357604051634f4b461f60e11b815260040160405180910390fd5b6004810154156108c65760405163632a22bb60e01b815260040160405180910390fd5b856108e457604051636caad1ed60e11b815260040160405180910390fd5b5f6109488260060180548060200260200160405190810160405280929190818152602001828054801561093e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610920575b505050505061202b565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa15801561099a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109c19190810190613dab565b9050806101c00151156109de576109de8b828a858b8b8b8b612131565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610a3c575f5ffd5b505af1158015610a4e573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610a8f96959493929190613f6d565b60405180910390a25050505050505050505050565b610aac61232d565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610b1d57610b1d613c2f565b604051908082528060200260200182016040528015610b46578160200160208202803683370190505b509450806001600160401b03811115610b6157610b61613c2f565b604051908082528060200260200182016040528015610b8a578160200160208202803683370190505b5093505f805b83811015610c86575f856006018281548110610bae57610bae613fba565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610bf457610bf4613c1b565b03610c7d5780888481518110610c0c57610c0c613fba565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610c6457610c64613fba565b602090810291909101015282610c7981613fce565b9350505b50600101610b90565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610cb457610cb4613c1b565b03610cd257604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d075760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d2b57610d2b613c1b565b14610d49576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610d90573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610db49190613fe6565b905080610dc76040860160208701614010565b63ffffffff161115610ddf6040860160208701614010565b829091610e0d576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610752565b5050815460ff1916600190811783558201859055436002830155600354610e349042614029565b6003830155610e48600583018560026137de565b50610e51611d3d565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ea2928a928a929161403c565b60405180910390a250600195945050505050565b5f610ec0826113d8565b610ecb57505f919050565b6001546001600160a01b0316610ef4576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f24908590600401613b40565b602060405180830381865afa158015610f3f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f63919061408c565b92915050565b5f818152600a602052604081206001815460ff166003811115610f8e57610f8e613c1b565b14610f9b57505f92915050565b6003015442111592915050565b610fb061232d565b6001600160a01b038116610fd75760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561103457611034613c1b565b149392505050565b61104461232d565b6001600160a01b03811661106b5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6110ba61232d565b6110c35f61235f565b565b600b545f9081906001600160a01b031633146110f45760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561111957611119613c1b565b1461113757604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561117757611177613c1b565b1461118757600b01549150611264565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916111bb83613c06565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161120c929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6112746113aa565b6001600160a01b0316336001600160a01b0316148061129d57506001546001600160a01b031633145b6112ba57604051632864c4e160e01b815260040160405180910390fd5b6112c3816113d8565b6113a75760048054600160281b900464ffffffffff16906112ed906001600160a01b0384166123cf565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161133e83613fce565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161081b565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611428576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561147e57602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611460575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156114c5576114c5613c1b565b14159392505050565b5f8181526009602052604090205460609081908190611500576040516322e679e360e11b815260040160405180910390fd5b5f848152600d60209081526040808320600e8352818420600f8452938290208154835181860281018601909452808452919493909291859183018282801561156557602002820191905f5260205f20905b815481526020019060010190808311611551575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156115b557602002820191905f5260205f20905b8154815260200190600101908083116115a1575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561160557602002820191905f5260205f20905b8154815260200190600101908083116115f1575b505050505090509250925092509193909250565b61162161232d565b6001600160a01b0381166116485760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f61169a6125a5565b805490915060ff600160401b82041615906001600160401b03165f811580156116c05750825b90505f826001600160401b031660011480156116db5750303b155b9050811580156116e9575080155b156117075760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561173157845460ff60401b1916600160401b1785555b6001600160a01b0387166117585760405163d92e233d60e01b815260040160405180910390fd5b611761336125cd565b61176d600460146125de565b61177686610aa4565b61177e6113aa565b6001600160a01b0316876001600160a01b03161461179f5761179f87611d4f565b83156117e557845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561181257611812613c1b565b0361183057604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561184857611848613c1b565b1461186657604051631860f69960e31b815260040160405180910390fd5b8060030154421161188a57604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061196f578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611950575f5ffd5b505af1158015611962573d5f5f3e3d5ffd5b505f979650505050505050565b6119788261265d565b815460ff191660021782556006820154600b83018190555f816001600160401b038111156119a8576119a8613c2f565b6040519080825280602002602001820160405280156119d1578160200160208202803683370190505b5090505f5b82811015611a4357846009015f8660060183815481106119f8576119f8613fba565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611a3057611a30613fba565b60209081029190910101526001016119d6565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611a86575f5ffd5b505af1158015611a98573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ea29291906140a5565b611ad961232d565b6001600160a01b038116611b005760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611b6d57611b6d613c1b565b03611b8b57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611ba357611ba3613c1b565b14611bc157604051631860f69960e31b815260040160405180910390fd5b8060030154421115611be657604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611c185760405163257309f160e11b815260040160405180910390fd5b611c2133610eb6565b611c3e5760405163149fbcfd60e11b815260040160405180910390fd5b611c49338385612783565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611cc890839083612954565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611d38576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611d4a60046014612b55565b905090565b611d5761232d565b6001600160a01b038116611d80575f604051631e4fbdf760e01b81526004016107529190613b40565b6113a78161235f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611dc85760405162461bcd60e51b8152600401610752906140b7565b825464ffffffffff600160281b90910481169082168111611e265760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610752565b825f5b81866001015f611e398488612c4e565b64ffffffffff1681526020019081526020015f20819055505f816001611e5f9190614101565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611e945750612023565b600185165f03611f5b575f611eb383611eae88600161411a565b612c4e565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f1491600401614137565b602060405180830381865af4158015611f2f573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f539190613fe6565b93505061200f565b5f611f6b83611eae600189614167565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611fcc91600401614137565b602060405180830381865af4158015611fe7573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061200b9190613fe6565b9350505b50647fffffffff600194851c169301611e29565b505050505050565b80515f908161203b826014614184565b6001600160401b0381111561205257612052613c2f565b6040519080825280601f01601f19166020018201604052801561207c576020820181803683370190505b5090505f5b82811015612121575f85828151811061209c5761209c613fba565b602002602001015160601b90505f8260146120b79190614184565b90505f5b6014811015612113578281601481106120d6576120d6613fba565b1a60f81b856120e58385614029565b815181106120f5576120f5613fba565b60200101906001600160f81b03191690815f1a9053506001016120bb565b505050806001019050612081565b5080516020909101209392505050565b8261214f57604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b8152600401612186949392919061419b565b602060405180830381865afa1580156121a1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121c5919061408c565b6121e25760405163051d8aa760e51b815260040160405180910390fd5b8061220057604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661222957604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b815260040161227897969594939291906141ba565b5f60405180830381865afa158015612292573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526122b99190810190614294565b5f8e8152600d6020908152604090912084519497509295509093506122e1929086019061387f565b505f8b8152600e6020908152604090912083516123009285019061387f565b505f8b8152600f60209081526040909120825161231f9284019061387f565b505050505050505050505050565b336123366113aa565b6001600160a01b0316146110c3573360405163118cdaa760e01b81526004016107529190613b40565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061241e5760405162461bcd60e51b8152600401610752906140b7565b825464ffffffffff908116908216106124715760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610752565b61247c81600161411a565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6124b38487612c4e565b64ffffffffff16815260208101919091526040015f2055600183161561259e575f6124e382611eae600187614167565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161254491600401614137565b602060405180830381865af415801561255f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125839190613fe6565b647fffffffff600195861c16949093509190910190506124a3565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f63565b6125d5612c6b565b6113a781612c90565b602060ff8216111561262c5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610752565b61263d600160ff831681901b614378565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b8181101561277e575f612678826001614029565b90505b82811015612775575f84600601838154811061269957612699613fba565b5f9182526020822001546006870180546001600160a01b03909216935090849081106126c7576126c7613fba565b5f918252602090912001546001600160a01b039081169150821681101561276b57808660060185815481106126fe576126fe613fba565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055508186600601848154811061273f5761273f613fba565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b505060010161267b565b50600101612664565b505050565b5f82116127a35760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166127cc576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161280291614378565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612849573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061286d9190613fe6565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128c0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128e49190613fe6565b90505f81116129065760405163aeaddff160e01b815260040160405180910390fd5b5f612911828461438b565b90505f81116129335760405163149fbcfd60e11b815260040160405180910390fd5b808611156117e55760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156129d257508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612b4e565b5f5f90505f876009015f855f815481106129ee576129ee613fba565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612a76575f896009015f878481548110612a3857612a38613fba565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612a6d578092508193505b50600101612a17565b50808610612a8a575f945050505050612b4e565b5f88600a015f868581548110612aa257612aa2613fba565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612adf57612adf613c1b565b021790555086848381548110612af757612af7613fba565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612ba85760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610752565b602060ff83161115612bcc5760405162461bcd60e51b8152600401610752906143aa565b8254600160281b900464ffffffffff1680612beb60ff851660026144fb565b64ffffffffff161015612c3b5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610752565b612c46848285612c98565b949350505050565b5f81612c6160ff851663ffffffff614514565b612b4e919061411a565b612c73612d60565b6110c357604051631afcd79f60e31b815260040160405180910390fd5b611d57612c6b565b5f602060ff83161115612cbd5760405162461bcd60e51b8152600401610752906143aa565b8264ffffffffff165f03612cdb57612cd482612d79565b9050612b4e565b5f612ce7836001614101565b60ff166001600160401b03811115612d0157612d01613c2f565b604051908082528060200260200182016040528015612d2a578160200160208202803683370190505b509050612d3985858584613413565b808360ff1681518110612d4e57612d4e613fba565b60200260200101519150509392505050565b5f612d696125a5565b54600160401b900460ff16919050565b5f8160ff165f03612d8b57505f919050565b8160ff16600103612dbd57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff16600203612def57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff16600303612e2157507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff16600403612e5357507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff16600503612e8557507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff16600603612eb757507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff16600703612ee957507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612f1b57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612f4d57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612f7f57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612fb157507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612fe357507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361301557507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361304757507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361307957507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036130ab57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff166011036130dd57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361310f57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361314157507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361317357507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036131a557507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff166016036131d757507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361320957507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361323b57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361326d57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361329f57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b036132d157507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361330357507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361333557507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361336757507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361339957507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036133cb57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610752565b602060ff831611156134375760405162461bcd60e51b8152600401610752906143aa565b5f8364ffffffffff161161349b5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610752565b5f6134a7600185614167565b9050600181165f036134fa57846001015f6134c25f84612c4e565b64ffffffffff1681526020019081526020015f2054825f815181106134e9576134e9613fba565b602002602001018181525050613522565b6135035f612d79565b825f8151811061351557613515613fba565b6020026020010181815250505b5f5b8360ff168160ff16101561202357600182165f0361361a5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061357657613576613fba565b6020026020010151815260200161358c85612d79565b8152506040518263ffffffff1660e01b81526004016135ab9190614137565b602060405180830381865af41580156135c6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135ea9190613fe6565b836135f6836001614101565b60ff168151811061360957613609613fba565b6020026020010181815250506137cb565b5f613626826001614101565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156136c8575f876001015f61367d85600161366c9190614101565b60018864ffffffffff16901c612c4e565b64ffffffffff1681526020019081526020015f2054905080858460016136a39190614101565b60ff16815181106136b6576136b6613fba565b602002602001018181525050506137c9565b5f876001015f6136df85600188611eae9190614167565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff168151811061373657613736613fba565b60200260200101518152506040518263ffffffff1660e01b815260040161375d9190614137565b602060405180830381865af4158015613778573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061379c9190613fe6565b856137a8856001614101565b60ff16815181106137bb576137bb613fba565b602002602001018181525050505b505b647fffffffff600192831c169101613524565b60018301918390821561386f579160200282015f5b8382111561383d57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026137f3565b801561386d5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261383d565b505b5061387b9291506138b8565b5090565b828054828255905f5260205f2090810192821561386f579160200282015b8281111561386f57825182559160200191906001019061389d565b5b8082111561387b575f81556001016138b9565b6001600160a01b03811681146113a7575f5ffd5b5f602082840312156138f0575f5ffd5b8135612b4e816138cc565b5f6020828403121561390b575f5ffd5b5035919050565b5f5f83601f840112613922575f5ffd5b5081356001600160401b03811115613938575f5ffd5b60208301915083602082850101111561394f575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b03121561396d575f5ffd5b8835975060208901356001600160401b03811115613989575f5ffd5b6139958b828c01613912565b9098509650506040890135945060608901356001600160401b038111156139ba575f5ffd5b6139c68b828c01613912565b90955093505060808901356001600160401b038111156139e4575f5ffd5b6139f08b828c01613912565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613a3d5781516001600160a01b0316865260209586019590910190600101613a16565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613a3d578151865260209586019590910190600101613a59565b604081525f613a896040830185613a04565b8281036020840152613a9b8185613a47565b95945050505050565b5f5f5f60808486031215613ab6575f5ffd5b833592506020840135915060808401851015613ad0575f5ffd5b6040840190509250925092565b5f5f60408385031215613aee575f5ffd5b823591506020830135613b00816138cc565b809150509250929050565b5f5f5f60608486031215613b1d575f5ffd5b833592506020840135613b2f816138cc565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612b4e6020830184613a04565b606081525f613b786060830186613a47565b8281036020840152613b8a8186613a47565b90508281036040840152613b9e8185613a47565b9695505050505050565b5f5f60408385031215613bb9575f5ffd5b8235613bc4816138cc565b946020939093013593505050565b5f5f60408385031215613be3575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613c1457613c14613bf2565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613c6657613c66613c2f565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613c9457613c94613c2f565b604052919050565b805160048110611d38575f5ffd5b5f82601f830112613cb9575f5ffd5b604080519081016001600160401b0381118282101715613cdb57613cdb613c2f565b8060405250806040840185811115613cf1575f5ffd5b845b81811015613d0b578051835260209283019201613cf3565b509195945050505050565b8051611d38816138cc565b805160ff81168114611d38575f5ffd5b5f82601f830112613d40575f5ffd5b81516001600160401b03811115613d5957613d59613c2f565b613d6c601f8201601f1916602001613c6c565b818152846020838601011115613d80575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611d38575f5ffd5b5f60208284031215613dbb575f5ffd5b81516001600160401b03811115613dd0575f5ffd5b82016102008185031215613de2575f5ffd5b613dea613c43565b81518152613dfa60208301613c9c565b602082015260408281015190820152613e168560608401613caa565b606082015260a08201516080820152613e3160c08301613d16565b60a0820152613e4260e08301613d21565b60c08201526101008201516001600160401b03811115613e60575f5ffd5b613e6c86828501613d31565b60e083015250613e7f6101208301613d16565b610100820152613e926101408301613d16565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613ec8575f5ffd5b613ed486828501613d31565b61018083015250613ee86101c08301613d16565b6101a0820152613efb6101e08301613d9c565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613a3d5781546001600160a01b0316865260209095019460019182019101613f1e565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f613f7f6080830189613f09565b8281036020840152613f9281888a613f45565b90508560408401528281036060840152613fad818587613f45565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60018201613fdf57613fdf613bf2565b5060010190565b5f60208284031215613ff6575f5ffd5b5051919050565b803563ffffffff81168114611d38575f5ffd5b5f60208284031215614020575f5ffd5b612b4e82613ffd565b80820180821115610f6357610f63613bf2565b84815260a0810160208201855f5b60028110156140775763ffffffff61406183613ffd565b168352602092830192919091019060010161404a565b50505060608201939093526080015292915050565b5f6020828403121561409c575f5ffd5b612b4e82613d9c565b604081525f613a896040830185613f09565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f6357610f63613bf2565b64ffffffffff8181168382160190811115610f6357610f63613bf2565b6040810181835f5b600281101561415e57815183526020928301929091019060010161413f565b50505092915050565b64ffffffffff8281168282160390811115610f6357610f63613bf2565b8082028115828204841417610f6357610f63613bf2565b848152836020820152606060408201525f613b9e606083018486613f45565b60018060a01b038816815286602082015285604082015260a060608201525f6141e760a083018688613f45565b82810360808401526141fa818587613f45565b9a9950505050505050505050565b5f6001600160401b0382111561422057614220613c2f565b5060051b60200190565b5f82601f830112614239575f5ffd5b815161424c61424782614208565b613c6c565b8082825260208201915060208360051b86010192508583111561426d575f5ffd5b602085015b8381101561428a578051835260209283019201614272565b5095945050505050565b5f5f5f606084860312156142a6575f5ffd5b83516001600160401b038111156142bb575f5ffd5b8401601f810186136142cb575f5ffd5b80516142d961424782614208565b8082825260208201915060208360051b8501019250888311156142fa575f5ffd5b6020840193505b8284101561431c578351825260209384019390910190614301565b8096505050505060208401516001600160401b0381111561433b575f5ffd5b6143478682870161422a565b92505060408401516001600160401b03811115614362575f5ffd5b61436e8682870161422a565b9150509250925092565b81810381811115610f6357610f63613bf2565b5f826143a557634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156112645780850481111561440c5761440c613bf2565b600184161561441a57908102905b60019390931c9280026143f1565b5f8261443657506001610f63565b8161444257505f610f63565b8160018114614458576002811461446257614494565b6001915050610f63565b60ff84111561447357614473613bf2565b6001841b915064ffffffffff82111561448e5761448e613bf2565b50610f63565b5060208310610133831016604e8410600b84101617156144cc575081810a64ffffffffff8111156144c7576144c7613bf2565b610f63565b6144dc64ffffffffff84846143ed565b8064ffffffffff048211156144f3576144f3613bf2565b029392505050565b5f612b4e64ffffffffff841664ffffffffff8416614428565b64ffffffffff818116838216029081169081811461453457614534613bf2565b509291505056fea164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6145d3806100d65f395ff3fe608060405234801561000f575f5ffd5b5060043610610276575f3560e01c80639a7a2ffc11610156578063dbb06c93116100ca578063f165053611610084578063f165053614610606578063f2fde38b14610620578063f379b0df14610633578063f52fd8031461066d578063f6fc05d5146106dd578063fa932569146106e6575f5ffd5b8063dbb06c93146105a0578063e4be6e3d146105b2578063e59e4695146105c5578063e6745e13146105d8578063e82f3b70146105eb578063ebf0c717146105fe575f5ffd5b8063bff232c11161011b578063bff232c114610518578063c2b40ae41461052b578063c3a0ec301461054a578063ca2869a01461055b578063cd6dc6871461057a578063da881e5a1461058d575f5ffd5b80639a7a2ffc1461047e5780639f0f874a146104ba578063a0164930146104c3578063a8a4d69b146104e3578063b8ab4704146104f6575f5ffd5b80635d504776116101ed5780638a78bb15116101b25780638a78bb15146104085780638cb89ecb1461041b5780638d1ddfb11461043a5780638da5cb5b146104505780638e5ce3ad146104585780639015d3711461046b575f5ffd5b80635d5047761461038d57806370e36bbe146103a0578063715018a6146103b35780637c92f524146103bb57806385814243146103e8575f5ffd5b80632800d8291161023e5780632800d829146102fc578063291a691b1461030f5780632e7b716d146103325780634d6861a61461034557806350e6d94c146103585780635302670f1461037a575f5ffd5b8063096b810a1461027a578063099a161a1461028f5780630bbfade7146102b55780630f3e3412146102c857806317d61120146102db575b5f5ffd5b61028d61028836600461396b565b6106f9565b005b6102a261029d366004613986565b610845565b6040519081526020015b60405180910390f35b61028d6102c33660046139e1565b61087e565b61028d6102d6366004613986565b610ac2565b6102ee6102e9366004613986565b610b05565b6040516102ac929190613b02565b6102a261030a366004613986565b610cae565b61032261031d366004613b2f565b610cfa565b60405190151581526020016102ac565b61032261034036600461396b565b610ed4565b610322610353366004613986565b610f87565b61032261036636600461396b565b60066020525f908152604090205460ff1681565b61028d61038836600461396b565b610fc6565b61032261039b366004613b68565b611017565b61028d6103ae36600461396b565b61105a565b61028d6110d0565b6103ce6103c9366004613b96565b6110e3565b6040805192835263ffffffff9091166020830152016102ac565b6001546103fb906001600160a01b031681565b6040516102ac9190613bcb565b61028d61041636600461396b565b61128a565b6102a2610429366004613986565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a2565b6103fb6113c8565b600b546103fb906001600160a01b031681565b61032261047936600461396b565b6113f6565b6104a461048c36600461396b565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ac565b6102a260035481565b6104d66104d1366004613986565b611413565b6040516102ac9190613bdf565b6103226104f1366004613b68565b6114a9565b610509610504366004613986565b6114ec565b6040516102ac93929190613bf1565b61028d61052636600461396b565b611637565b6102a2610539366004613986565b60086020525f908152604090205481565b6001546001600160a01b03166103fb565b6102a2610569366004613986565b5f9081526008602052604090205490565b61028d610588366004613c33565b6116af565b61032261059b366004613986565b61180c565b5f546103fb906001600160a01b031681565b600c546103fb906001600160a01b031681565b61028d6105d336600461396b565b611aef565b61028d6105e6366004613c5d565b611b67565b6102a26105f9366004613986565b611d2a565b6102a2611d5b565b61060e601481565b60405160ff90911681526020016102ac565b61028d61062e36600461396b565b611d6d565b60045461064f9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ac565b6106ae61067b366004613986565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ac949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a260025481565b6103fb6106f4366004613c5d565b611da7565b6107016113c8565b6001600160a01b0316336001600160a01b0316148061072a57506001546001600160a01b031633145b61074757604051632864c4e160e01b815260040160405180910390fd5b610750816113f6565b8190610779576040516381e5828960e01b81526004016107709190613bcb565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107a79060049083611e14565b6001600160a01b0382165f908152600660205260408120805460ff1916905560028054916107d483613c91565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a602052604081206004810154610874576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b5f888152600a602052604090206002815460ff1660038111156108a3576108a3613ca6565b146108c157604051634f4b461f60e11b815260040160405180910390fd5b6004810154156108e45760405163632a22bb60e01b815260040160405180910390fd5b8561090257604051636caad1ed60e11b815260040160405180910390fd5b5f6109668260060180548060200260200160405190810160405280929190818152602001828054801561095c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161093e575b50505050506120b6565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa1580156109b8573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109df9190810190613e36565b9050806101c00151156109fc576109fc8b828a858b8b8b8b6121bc565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610a5a575f5ffd5b505af1158015610a6c573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610aad96959493929190613ff8565b60405180910390a25050505050505050505050565b610aca6123b8565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610b3b57610b3b613cba565b604051908082528060200260200182016040528015610b64578160200160208202803683370190505b509450806001600160401b03811115610b7f57610b7f613cba565b604051908082528060200260200182016040528015610ba8578160200160208202803683370190505b5093505f805b83811015610ca4575f856006018281548110610bcc57610bcc614045565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610c1257610c12613ca6565b03610c9b5780888481518110610c2a57610c2a614045565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610c8257610c82614045565b602090810291909101015282610c9781614059565b9350505b50600101610bae565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610cd257610cd2613ca6565b03610cf057604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d255760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d4957610d49613ca6565b14610d67576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610dae573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd29190614071565b905080610de5604086016020870161409b565b63ffffffff161115610dfd604086016020870161409b565b829091610e2b576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610770565b5050815460ff1916600190811783558201859055436002830155600354610e5290426140b4565b6003830155610e6660058301856002613869565b50610e6f611d5b565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ec0928a928a92916140c7565b60405180910390a250600195945050505050565b5f610ede826113f6565b610ee957505f919050565b6001546001600160a01b0316610f12576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f42908590600401613bcb565b602060405180830381865afa158015610f5d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f819190614117565b92915050565b5f818152600a602052604081206001815460ff166003811115610fac57610fac613ca6565b14610fb957505f92915050565b6003015442111592915050565b610fce6123b8565b6001600160a01b038116610ff55760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561105257611052613ca6565b149392505050565b6110626123b8565b6001600160a01b0381166110895760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6110d86123b8565b6110e15f6123ea565b565b600b545f9081906001600160a01b031633146111125760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561113757611137613ca6565b1461115557604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561119557611195613ca6565b146111a557600b01549150611282565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916111d983613c91565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161122a929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6112926113c8565b6001600160a01b0316336001600160a01b031614806112bb57506001546001600160a01b031633145b6112d857604051632864c4e160e01b815260040160405180910390fd5b6112e1816113f6565b6113c55760048054600160281b900464ffffffffff169061130b906001600160a01b03841661245a565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161135c83614059565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db5390606001610839565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611446576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561149c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161147e575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156114e3576114e3613ca6565b14159392505050565b5f818152600960205260409020546060908190819061151e576040516322e679e360e11b815260040160405180910390fd5b5f848152600d60209081526040808320600e8352818420600f8452938290208154835181860281018601909452808452919493909291859183018282801561158357602002820191905f5260205f20905b81548152602001906001019080831161156f575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156115d357602002820191905f5260205f20905b8154815260200190600101908083116115bf575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561162357602002820191905f5260205f20905b81548152602001906001019080831161160f575b505050505090509250925092509193909250565b61163f6123b8565b6001600160a01b0381166116665760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f6116b8612630565b805490915060ff600160401b82041615906001600160401b03165f811580156116de5750825b90505f826001600160401b031660011480156116f95750303b155b905081158015611707575080155b156117255760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561174f57845460ff60401b1916600160401b1785555b6001600160a01b0387166117765760405163d92e233d60e01b815260040160405180910390fd5b61177f33612658565b61178b60046014612669565b61179486610ac2565b61179c6113c8565b6001600160a01b0316876001600160a01b0316146117bd576117bd87611d6d565b831561180357845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561183057611830613ca6565b0361184e57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561186657611866613ca6565b1461188457604051631860f69960e31b815260040160405180910390fd5b806003015442116118a857604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061198d578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b15801561196e575f5ffd5b505af1158015611980573d5f5f3e3d5ffd5b505f979650505050505050565b611996826126e8565b815460ff191660021782556006820154600b83018190555f816001600160401b038111156119c6576119c6613cba565b6040519080825280602002602001820160405280156119ef578160200160208202803683370190505b5090505f5b82811015611a6157846009015f866006018381548110611a1657611a16614045565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611a4e57611a4e614045565b60209081029190910101526001016119f4565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611aa4575f5ffd5b505af1158015611ab6573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ec0929190614130565b611af76123b8565b6001600160a01b038116611b1e5760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611b8b57611b8b613ca6565b03611ba957604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611bc157611bc1613ca6565b14611bdf57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611c0457604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611c365760405163257309f160e11b815260040160405180910390fd5b611c3f33610ed4565b611c5c5760405163149fbcfd60e11b815260040160405180910390fd5b611c6733838561280e565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611ce6908390836129df565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611d56576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611d6860046014612be0565b905090565b611d756123b8565b6001600160a01b038116611d9e575f604051631e4fbdf760e01b81526004016107709190613bcb565b6113c5816123ea565b5f828152600a6020526040812060060180548390808210611de4576040516326c5c55b60e11b815260048101929092526024820152604401610770565b5050808381548110611df857611df8614045565b5f918252602090912001546001600160a01b0316949350505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611e535760405162461bcd60e51b815260040161077090614142565b825464ffffffffff600160281b90910481169082168111611eb15760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610770565b825f5b81866001015f611ec48488612cd9565b64ffffffffff1681526020019081526020015f20819055505f816001611eea919061418c565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611f1f57506120ae565b600185165f03611fe6575f611f3e83611f398860016141a5565b612cd9565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f9f916004016141c2565b602060405180830381865af4158015611fba573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fde9190614071565b93505061209a565b5f611ff683611f396001896141f2565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612057916004016141c2565b602060405180830381865af4158015612072573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120969190614071565b9350505b50647fffffffff600194851c169301611eb4565b505050505050565b80515f90816120c682601461420f565b6001600160401b038111156120dd576120dd613cba565b6040519080825280601f01601f191660200182016040528015612107576020820181803683370190505b5090505f5b828110156121ac575f85828151811061212757612127614045565b602002602001015160601b90505f826014612142919061420f565b90505f5b601481101561219e5782816014811061216157612161614045565b1a60f81b8561217083856140b4565b8151811061218057612180614045565b60200101906001600160f81b03191690815f1a905350600101612146565b50505080600101905061210c565b5080516020909101209392505050565b826121da57604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b81526004016122119493929190614226565b602060405180830381865afa15801561222c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122509190614117565b61226d5760405163051d8aa760e51b815260040160405180910390fd5b8061228b57604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b03166122b457604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b81526004016123039796959493929190614245565b5f60405180830381865afa15801561231d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612344919081019061431f565b5f8e8152600d60209081526040909120845194975092955090935061236c929086019061390a565b505f8b8152600e60209081526040909120835161238b9285019061390a565b505f8b8152600f6020908152604090912082516123aa9284019061390a565b505050505050505050505050565b336123c16113c8565b6001600160a01b0316146110e1573360405163118cdaa760e01b81526004016107709190613bcb565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106124a95760405162461bcd60e51b815260040161077090614142565b825464ffffffffff908116908216106124fc5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610770565b6125078160016141a5565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f61253e8487612cd9565b64ffffffffff16815260208101919091526040015f20556001831615612629575f61256e82611f396001876141f2565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916125cf916004016141c2565b602060405180830381865af41580156125ea573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061260e9190614071565b647fffffffff600195861c169490935091909101905061252e565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f81565b612660612cf6565b6113c581612d1b565b602060ff821611156126b75760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610770565b6126c8600160ff831681901b614403565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612809575f6127038260016140b4565b90505b82811015612800575f84600601838154811061272457612724614045565b5f9182526020822001546006870180546001600160a01b039092169350908490811061275257612752614045565b5f918252602090912001546001600160a01b03908116915082168110156127f6578086600601858154811061278957612789614045565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550818660060184815481106127ca576127ca614045565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612706565b506001016126ef565b505050565b5f821161282e5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612857576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161288d91614403565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156128d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128f89190614071565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561294b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061296f9190614071565b90505f81116129915760405163aeaddff160e01b815260040160405180910390fd5b5f61299c8284614416565b90505f81116129be5760405163149fbcfd60e11b815260040160405180910390fd5b808611156118035760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612a5d57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612bd9565b5f5f90505f876009015f855f81548110612a7957612a79614045565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612b01575f896009015f878481548110612ac357612ac3614045565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612af8578092508193505b50600101612aa2565b50808610612b15575f945050505050612bd9565b5f88600a015f868581548110612b2d57612b2d614045565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612b6a57612b6a613ca6565b021790555086848381548110612b8257612b82614045565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612c335760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610770565b602060ff83161115612c575760405162461bcd60e51b815260040161077090614435565b8254600160281b900464ffffffffff1680612c7660ff85166002614586565b64ffffffffff161015612cc65760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610770565b612cd1848285612d23565b949350505050565b5f81612cec60ff851663ffffffff61459f565b612bd991906141a5565b612cfe612deb565b6110e157604051631afcd79f60e31b815260040160405180910390fd5b611d75612cf6565b5f602060ff83161115612d485760405162461bcd60e51b815260040161077090614435565b8264ffffffffff165f03612d6657612d5f82612e04565b9050612bd9565b5f612d7283600161418c565b60ff166001600160401b03811115612d8c57612d8c613cba565b604051908082528060200260200182016040528015612db5578160200160208202803683370190505b509050612dc48585858461349e565b808360ff1681518110612dd957612dd9614045565b60200260200101519150509392505050565b5f612df4612630565b54600160401b900460ff16919050565b5f8160ff165f03612e1657505f919050565b8160ff16600103612e4857507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff16600203612e7a57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff16600303612eac57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff16600403612ede57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff16600503612f1057507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff16600603612f4257507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff16600703612f7457507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612fa657507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612fd857507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361300a57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361303c57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361306e57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036130a057507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036130d257507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361310457507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361313657507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361316857507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361319a57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036131cc57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036131fe57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361323057507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361326257507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361329457507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036132c657507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036132f857507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361332a57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361335c57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361338e57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036133c057507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036133f257507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361342457507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361345657507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610770565b602060ff831611156134c25760405162461bcd60e51b815260040161077090614435565b5f8364ffffffffff16116135265760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610770565b5f6135326001856141f2565b9050600181165f0361358557846001015f61354d5f84612cd9565b64ffffffffff1681526020019081526020015f2054825f8151811061357457613574614045565b6020026020010181815250506135ad565b61358e5f612e04565b825f815181106135a0576135a0614045565b6020026020010181815250505b5f5b8360ff168160ff1610156120ae57600182165f036136a55773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061360157613601614045565b6020026020010151815260200161361785612e04565b8152506040518263ffffffff1660e01b815260040161363691906141c2565b602060405180830381865af4158015613651573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136759190614071565b8361368183600161418c565b60ff168151811061369457613694614045565b602002602001018181525050613856565b5f6136b182600161418c565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613753575f876001015f6137088560016136f7919061418c565b60018864ffffffffff16901c612cd9565b64ffffffffff1681526020019081526020015f20549050808584600161372e919061418c565b60ff168151811061374157613741614045565b60200260200101818152505050613854565b5f876001015f61376a85600188611f3991906141f2565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106137c1576137c1614045565b60200260200101518152506040518263ffffffff1660e01b81526004016137e891906141c2565b602060405180830381865af4158015613803573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138279190614071565b8561383385600161418c565b60ff168151811061384657613846614045565b602002602001018181525050505b505b647fffffffff600192831c1691016135af565b6001830191839082156138fa579160200282015f5b838211156138c857833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030261387e565b80156138f85782816101000a81549063ffffffff02191690556004016020816003010492830192600103026138c8565b505b50613906929150613943565b5090565b828054828255905f5260205f209081019282156138fa579160200282015b828111156138fa578251825591602001919060010190613928565b5b80821115613906575f8155600101613944565b6001600160a01b03811681146113c5575f5ffd5b5f6020828403121561397b575f5ffd5b8135612bd981613957565b5f60208284031215613996575f5ffd5b5035919050565b5f5f83601f8401126139ad575f5ffd5b5081356001600160401b038111156139c3575f5ffd5b6020830191508360208285010111156139da575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b0312156139f8575f5ffd5b8835975060208901356001600160401b03811115613a14575f5ffd5b613a208b828c0161399d565b9098509650506040890135945060608901356001600160401b03811115613a45575f5ffd5b613a518b828c0161399d565b90955093505060808901356001600160401b03811115613a6f575f5ffd5b613a7b8b828c0161399d565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613ac85781516001600160a01b0316865260209586019590910190600101613aa1565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613ac8578151865260209586019590910190600101613ae4565b604081525f613b146040830185613a8f565b8281036020840152613b268185613ad2565b95945050505050565b5f5f5f60808486031215613b41575f5ffd5b833592506020840135915060808401851015613b5b575f5ffd5b6040840190509250925092565b5f5f60408385031215613b79575f5ffd5b823591506020830135613b8b81613957565b809150509250929050565b5f5f5f60608486031215613ba8575f5ffd5b833592506020840135613bba81613957565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612bd96020830184613a8f565b606081525f613c036060830186613ad2565b8281036020840152613c158186613ad2565b90508281036040840152613c298185613ad2565b9695505050505050565b5f5f60408385031215613c44575f5ffd5b8235613c4f81613957565b946020939093013593505050565b5f5f60408385031215613c6e575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613c9f57613c9f613c7d565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613cf157613cf1613cba565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613d1f57613d1f613cba565b604052919050565b805160048110611d56575f5ffd5b5f82601f830112613d44575f5ffd5b604080519081016001600160401b0381118282101715613d6657613d66613cba565b8060405250806040840185811115613d7c575f5ffd5b845b81811015613d96578051835260209283019201613d7e565b509195945050505050565b8051611d5681613957565b805160ff81168114611d56575f5ffd5b5f82601f830112613dcb575f5ffd5b81516001600160401b03811115613de457613de4613cba565b613df7601f8201601f1916602001613cf7565b818152846020838601011115613e0b575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611d56575f5ffd5b5f60208284031215613e46575f5ffd5b81516001600160401b03811115613e5b575f5ffd5b82016102008185031215613e6d575f5ffd5b613e75613cce565b81518152613e8560208301613d27565b602082015260408281015190820152613ea18560608401613d35565b606082015260a08201516080820152613ebc60c08301613da1565b60a0820152613ecd60e08301613dac565b60c08201526101008201516001600160401b03811115613eeb575f5ffd5b613ef786828501613dbc565b60e083015250613f0a6101208301613da1565b610100820152613f1d6101408301613da1565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613f53575f5ffd5b613f5f86828501613dbc565b61018083015250613f736101c08301613da1565b6101a0820152613f866101e08301613e27565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613ac85781546001600160a01b0316865260209095019460019182019101613fa9565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f61400a6080830189613f94565b828103602084015261401d81888a613fd0565b90508560408401528281036060840152614038818587613fd0565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161406a5761406a613c7d565b5060010190565b5f60208284031215614081575f5ffd5b5051919050565b803563ffffffff81168114611d56575f5ffd5b5f602082840312156140ab575f5ffd5b612bd982614088565b80820180821115610f8157610f81613c7d565b84815260a0810160208201855f5b60028110156141025763ffffffff6140ec83614088565b16835260209283019291909101906001016140d5565b50505060608201939093526080015292915050565b5f60208284031215614127575f5ffd5b612bd982613e27565b604081525f613b146040830185613f94565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f8157610f81613c7d565b64ffffffffff8181168382160190811115610f8157610f81613c7d565b6040810181835f5b60028110156141e95781518352602092830192909101906001016141ca565b50505092915050565b64ffffffffff8281168282160390811115610f8157610f81613c7d565b8082028115828204841417610f8157610f81613c7d565b848152836020820152606060408201525f613c29606083018486613fd0565b60018060a01b038816815286602082015285604082015260a060608201525f61427260a083018688613fd0565b8281036080840152614285818587613fd0565b9a9950505050505050505050565b5f6001600160401b038211156142ab576142ab613cba565b5060051b60200190565b5f82601f8301126142c4575f5ffd5b81516142d76142d282614293565b613cf7565b8082825260208201915060208360051b8601019250858311156142f8575f5ffd5b602085015b838110156143155780518352602092830192016142fd565b5095945050505050565b5f5f5f60608486031215614331575f5ffd5b83516001600160401b03811115614346575f5ffd5b8401601f81018613614356575f5ffd5b80516143646142d282614293565b8082825260208201915060208360051b850101925088831115614385575f5ffd5b6020840193505b828410156143a757835182526020938401939091019061438c565b8096505050505060208401516001600160401b038111156143c6575f5ffd5b6143d2868287016142b5565b92505060408401516001600160401b038111156143ed575f5ffd5b6143f9868287016142b5565b9150509250925092565b81810381811115610f8157610f81613c7d565b5f8261443057634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156112825780850481111561449757614497613c7d565b60018416156144a557908102905b60019390931c92800261447c565b5f826144c157506001610f81565b816144cd57505f610f81565b81600181146144e357600281146144ed5761451f565b6001915050610f81565b60ff8411156144fe576144fe613c7d565b6001841b915064ffffffffff82111561451957614519613c7d565b50610f81565b5060208310610133831016604e8410600b8410161715614557575081810a64ffffffffff81111561455257614552613c7d565b610f81565b61456764ffffffffff8484614478565b8064ffffffffff0482111561457e5761457e613c7d565b029392505050565b5f612bd964ffffffffff841664ffffffffff84166144b3565b64ffffffffff81811683821602908116908181146145bf576145bf613c7d565b509291505056fea164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b5060043610610276575f3560e01c80639a7a2ffc11610156578063dbb06c93116100ca578063f165053611610084578063f165053614610606578063f2fde38b14610620578063f379b0df14610633578063f52fd8031461066d578063f6fc05d5146106dd578063fa932569146106e6575f5ffd5b8063dbb06c93146105a0578063e4be6e3d146105b2578063e59e4695146105c5578063e6745e13146105d8578063e82f3b70146105eb578063ebf0c717146105fe575f5ffd5b8063bff232c11161011b578063bff232c114610518578063c2b40ae41461052b578063c3a0ec301461054a578063ca2869a01461055b578063cd6dc6871461057a578063da881e5a1461058d575f5ffd5b80639a7a2ffc1461047e5780639f0f874a146104ba578063a0164930146104c3578063a8a4d69b146104e3578063b8ab4704146104f6575f5ffd5b80635d504776116101ed5780638a78bb15116101b25780638a78bb15146104085780638cb89ecb1461041b5780638d1ddfb11461043a5780638da5cb5b146104505780638e5ce3ad146104585780639015d3711461046b575f5ffd5b80635d5047761461038d57806370e36bbe146103a0578063715018a6146103b35780637c92f524146103bb57806385814243146103e8575f5ffd5b80632800d8291161023e5780632800d829146102fc578063291a691b1461030f5780632e7b716d146103325780634d6861a61461034557806350e6d94c146103585780635302670f1461037a575f5ffd5b8063096b810a1461027a578063099a161a1461028f5780630bbfade7146102b55780630f3e3412146102c857806317d61120146102db575b5f5ffd5b61028d61028836600461396b565b6106f9565b005b6102a261029d366004613986565b610845565b6040519081526020015b60405180910390f35b61028d6102c33660046139e1565b61087e565b61028d6102d6366004613986565b610ac2565b6102ee6102e9366004613986565b610b05565b6040516102ac929190613b02565b6102a261030a366004613986565b610cae565b61032261031d366004613b2f565b610cfa565b60405190151581526020016102ac565b61032261034036600461396b565b610ed4565b610322610353366004613986565b610f87565b61032261036636600461396b565b60066020525f908152604090205460ff1681565b61028d61038836600461396b565b610fc6565b61032261039b366004613b68565b611017565b61028d6103ae36600461396b565b61105a565b61028d6110d0565b6103ce6103c9366004613b96565b6110e3565b6040805192835263ffffffff9091166020830152016102ac565b6001546103fb906001600160a01b031681565b6040516102ac9190613bcb565b61028d61041636600461396b565b61128a565b6102a2610429366004613986565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a2565b6103fb6113c8565b600b546103fb906001600160a01b031681565b61032261047936600461396b565b6113f6565b6104a461048c36600461396b565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ac565b6102a260035481565b6104d66104d1366004613986565b611413565b6040516102ac9190613bdf565b6103226104f1366004613b68565b6114a9565b610509610504366004613986565b6114ec565b6040516102ac93929190613bf1565b61028d61052636600461396b565b611637565b6102a2610539366004613986565b60086020525f908152604090205481565b6001546001600160a01b03166103fb565b6102a2610569366004613986565b5f9081526008602052604090205490565b61028d610588366004613c33565b6116af565b61032261059b366004613986565b61180c565b5f546103fb906001600160a01b031681565b600c546103fb906001600160a01b031681565b61028d6105d336600461396b565b611aef565b61028d6105e6366004613c5d565b611b67565b6102a26105f9366004613986565b611d2a565b6102a2611d5b565b61060e601481565b60405160ff90911681526020016102ac565b61028d61062e36600461396b565b611d6d565b60045461064f9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ac565b6106ae61067b366004613986565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ac949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a260025481565b6103fb6106f4366004613c5d565b611da7565b6107016113c8565b6001600160a01b0316336001600160a01b0316148061072a57506001546001600160a01b031633145b61074757604051632864c4e160e01b815260040160405180910390fd5b610750816113f6565b8190610779576040516381e5828960e01b81526004016107709190613bcb565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107a79060049083611e14565b6001600160a01b0382165f908152600660205260408120805460ff1916905560028054916107d483613c91565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a602052604081206004810154610874576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b5f888152600a602052604090206002815460ff1660038111156108a3576108a3613ca6565b146108c157604051634f4b461f60e11b815260040160405180910390fd5b6004810154156108e45760405163632a22bb60e01b815260040160405180910390fd5b8561090257604051636caad1ed60e11b815260040160405180910390fd5b5f6109668260060180548060200260200160405190810160405280929190818152602001828054801561095c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161093e575b50505050506120b6565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa1580156109b8573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109df9190810190613e36565b9050806101c00151156109fc576109fc8b828a858b8b8b8b6121bc565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610a5a575f5ffd5b505af1158015610a6c573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610aad96959493929190613ff8565b60405180910390a25050505050505050505050565b610aca6123b8565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610b3b57610b3b613cba565b604051908082528060200260200182016040528015610b64578160200160208202803683370190505b509450806001600160401b03811115610b7f57610b7f613cba565b604051908082528060200260200182016040528015610ba8578160200160208202803683370190505b5093505f805b83811015610ca4575f856006018281548110610bcc57610bcc614045565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610c1257610c12613ca6565b03610c9b5780888481518110610c2a57610c2a614045565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610c8257610c82614045565b602090810291909101015282610c9781614059565b9350505b50600101610bae565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610cd257610cd2613ca6565b03610cf057604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d255760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d4957610d49613ca6565b14610d67576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610dae573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd29190614071565b905080610de5604086016020870161409b565b63ffffffff161115610dfd604086016020870161409b565b829091610e2b576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610770565b5050815460ff1916600190811783558201859055436002830155600354610e5290426140b4565b6003830155610e6660058301856002613869565b50610e6f611d5b565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ec0928a928a92916140c7565b60405180910390a250600195945050505050565b5f610ede826113f6565b610ee957505f919050565b6001546001600160a01b0316610f12576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f42908590600401613bcb565b602060405180830381865afa158015610f5d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f819190614117565b92915050565b5f818152600a602052604081206001815460ff166003811115610fac57610fac613ca6565b14610fb957505f92915050565b6003015442111592915050565b610fce6123b8565b6001600160a01b038116610ff55760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561105257611052613ca6565b149392505050565b6110626123b8565b6001600160a01b0381166110895760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6110d86123b8565b6110e15f6123ea565b565b600b545f9081906001600160a01b031633146111125760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561113757611137613ca6565b1461115557604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561119557611195613ca6565b146111a557600b01549150611282565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916111d983613c91565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161122a929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6112926113c8565b6001600160a01b0316336001600160a01b031614806112bb57506001546001600160a01b031633145b6112d857604051632864c4e160e01b815260040160405180910390fd5b6112e1816113f6565b6113c55760048054600160281b900464ffffffffff169061130b906001600160a01b03841661245a565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161135c83614059565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db5390606001610839565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611446576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561149c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161147e575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156114e3576114e3613ca6565b14159392505050565b5f818152600960205260409020546060908190819061151e576040516322e679e360e11b815260040160405180910390fd5b5f848152600d60209081526040808320600e8352818420600f8452938290208154835181860281018601909452808452919493909291859183018282801561158357602002820191905f5260205f20905b81548152602001906001019080831161156f575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156115d357602002820191905f5260205f20905b8154815260200190600101908083116115bf575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561162357602002820191905f5260205f20905b81548152602001906001019080831161160f575b505050505090509250925092509193909250565b61163f6123b8565b6001600160a01b0381166116665760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f6116b8612630565b805490915060ff600160401b82041615906001600160401b03165f811580156116de5750825b90505f826001600160401b031660011480156116f95750303b155b905081158015611707575080155b156117255760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561174f57845460ff60401b1916600160401b1785555b6001600160a01b0387166117765760405163d92e233d60e01b815260040160405180910390fd5b61177f33612658565b61178b60046014612669565b61179486610ac2565b61179c6113c8565b6001600160a01b0316876001600160a01b0316146117bd576117bd87611d6d565b831561180357845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561183057611830613ca6565b0361184e57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561186657611866613ca6565b1461188457604051631860f69960e31b815260040160405180910390fd5b806003015442116118a857604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061198d578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b15801561196e575f5ffd5b505af1158015611980573d5f5f3e3d5ffd5b505f979650505050505050565b611996826126e8565b815460ff191660021782556006820154600b83018190555f816001600160401b038111156119c6576119c6613cba565b6040519080825280602002602001820160405280156119ef578160200160208202803683370190505b5090505f5b82811015611a6157846009015f866006018381548110611a1657611a16614045565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611a4e57611a4e614045565b60209081029190910101526001016119f4565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611aa4575f5ffd5b505af1158015611ab6573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ec0929190614130565b611af76123b8565b6001600160a01b038116611b1e5760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611b8b57611b8b613ca6565b03611ba957604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611bc157611bc1613ca6565b14611bdf57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611c0457604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611c365760405163257309f160e11b815260040160405180910390fd5b611c3f33610ed4565b611c5c5760405163149fbcfd60e11b815260040160405180910390fd5b611c6733838561280e565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611ce6908390836129df565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611d56576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611d6860046014612be0565b905090565b611d756123b8565b6001600160a01b038116611d9e575f604051631e4fbdf760e01b81526004016107709190613bcb565b6113c5816123ea565b5f828152600a6020526040812060060180548390808210611de4576040516326c5c55b60e11b815260048101929092526024820152604401610770565b5050808381548110611df857611df8614045565b5f918252602090912001546001600160a01b0316949350505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611e535760405162461bcd60e51b815260040161077090614142565b825464ffffffffff600160281b90910481169082168111611eb15760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610770565b825f5b81866001015f611ec48488612cd9565b64ffffffffff1681526020019081526020015f20819055505f816001611eea919061418c565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611f1f57506120ae565b600185165f03611fe6575f611f3e83611f398860016141a5565b612cd9565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f9f916004016141c2565b602060405180830381865af4158015611fba573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fde9190614071565b93505061209a565b5f611ff683611f396001896141f2565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612057916004016141c2565b602060405180830381865af4158015612072573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120969190614071565b9350505b50647fffffffff600194851c169301611eb4565b505050505050565b80515f90816120c682601461420f565b6001600160401b038111156120dd576120dd613cba565b6040519080825280601f01601f191660200182016040528015612107576020820181803683370190505b5090505f5b828110156121ac575f85828151811061212757612127614045565b602002602001015160601b90505f826014612142919061420f565b90505f5b601481101561219e5782816014811061216157612161614045565b1a60f81b8561217083856140b4565b8151811061218057612180614045565b60200101906001600160f81b03191690815f1a905350600101612146565b50505080600101905061210c565b5080516020909101209392505050565b826121da57604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b81526004016122119493929190614226565b602060405180830381865afa15801561222c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122509190614117565b61226d5760405163051d8aa760e51b815260040160405180910390fd5b8061228b57604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b03166122b457604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b81526004016123039796959493929190614245565b5f60405180830381865afa15801561231d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612344919081019061431f565b5f8e8152600d60209081526040909120845194975092955090935061236c929086019061390a565b505f8b8152600e60209081526040909120835161238b9285019061390a565b505f8b8152600f6020908152604090912082516123aa9284019061390a565b505050505050505050505050565b336123c16113c8565b6001600160a01b0316146110e1573360405163118cdaa760e01b81526004016107709190613bcb565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106124a95760405162461bcd60e51b815260040161077090614142565b825464ffffffffff908116908216106124fc5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610770565b6125078160016141a5565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f61253e8487612cd9565b64ffffffffff16815260208101919091526040015f20556001831615612629575f61256e82611f396001876141f2565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916125cf916004016141c2565b602060405180830381865af41580156125ea573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061260e9190614071565b647fffffffff600195861c169490935091909101905061252e565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f81565b612660612cf6565b6113c581612d1b565b602060ff821611156126b75760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610770565b6126c8600160ff831681901b614403565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612809575f6127038260016140b4565b90505b82811015612800575f84600601838154811061272457612724614045565b5f9182526020822001546006870180546001600160a01b039092169350908490811061275257612752614045565b5f918252602090912001546001600160a01b03908116915082168110156127f6578086600601858154811061278957612789614045565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550818660060184815481106127ca576127ca614045565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612706565b506001016126ef565b505050565b5f821161282e5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612857576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161288d91614403565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156128d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128f89190614071565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561294b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061296f9190614071565b90505f81116129915760405163aeaddff160e01b815260040160405180910390fd5b5f61299c8284614416565b90505f81116129be5760405163149fbcfd60e11b815260040160405180910390fd5b808611156118035760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612a5d57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612bd9565b5f5f90505f876009015f855f81548110612a7957612a79614045565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612b01575f896009015f878481548110612ac357612ac3614045565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612af8578092508193505b50600101612aa2565b50808610612b15575f945050505050612bd9565b5f88600a015f868581548110612b2d57612b2d614045565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612b6a57612b6a613ca6565b021790555086848381548110612b8257612b82614045565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612c335760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610770565b602060ff83161115612c575760405162461bcd60e51b815260040161077090614435565b8254600160281b900464ffffffffff1680612c7660ff85166002614586565b64ffffffffff161015612cc65760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610770565b612cd1848285612d23565b949350505050565b5f81612cec60ff851663ffffffff61459f565b612bd991906141a5565b612cfe612deb565b6110e157604051631afcd79f60e31b815260040160405180910390fd5b611d75612cf6565b5f602060ff83161115612d485760405162461bcd60e51b815260040161077090614435565b8264ffffffffff165f03612d6657612d5f82612e04565b9050612bd9565b5f612d7283600161418c565b60ff166001600160401b03811115612d8c57612d8c613cba565b604051908082528060200260200182016040528015612db5578160200160208202803683370190505b509050612dc48585858461349e565b808360ff1681518110612dd957612dd9614045565b60200260200101519150509392505050565b5f612df4612630565b54600160401b900460ff16919050565b5f8160ff165f03612e1657505f919050565b8160ff16600103612e4857507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff16600203612e7a57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff16600303612eac57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff16600403612ede57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff16600503612f1057507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff16600603612f4257507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff16600703612f7457507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612fa657507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612fd857507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361300a57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361303c57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361306e57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036130a057507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036130d257507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361310457507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361313657507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361316857507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361319a57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036131cc57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036131fe57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361323057507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361326257507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361329457507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036132c657507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036132f857507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361332a57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361335c57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361338e57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036133c057507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036133f257507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361342457507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361345657507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610770565b602060ff831611156134c25760405162461bcd60e51b815260040161077090614435565b5f8364ffffffffff16116135265760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610770565b5f6135326001856141f2565b9050600181165f0361358557846001015f61354d5f84612cd9565b64ffffffffff1681526020019081526020015f2054825f8151811061357457613574614045565b6020026020010181815250506135ad565b61358e5f612e04565b825f815181106135a0576135a0614045565b6020026020010181815250505b5f5b8360ff168160ff1610156120ae57600182165f036136a55773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061360157613601614045565b6020026020010151815260200161361785612e04565b8152506040518263ffffffff1660e01b815260040161363691906141c2565b602060405180830381865af4158015613651573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136759190614071565b8361368183600161418c565b60ff168151811061369457613694614045565b602002602001018181525050613856565b5f6136b182600161418c565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613753575f876001015f6137088560016136f7919061418c565b60018864ffffffffff16901c612cd9565b64ffffffffff1681526020019081526020015f20549050808584600161372e919061418c565b60ff168151811061374157613741614045565b60200260200101818152505050613854565b5f876001015f61376a85600188611f3991906141f2565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106137c1576137c1614045565b60200260200101518152506040518263ffffffff1660e01b81526004016137e891906141c2565b602060405180830381865af4158015613803573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138279190614071565b8561383385600161418c565b60ff168151811061384657613846614045565b602002602001018181525050505b505b647fffffffff600192831c1691016135af565b6001830191839082156138fa579160200282015f5b838211156138c857833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030261387e565b80156138f85782816101000a81549063ffffffff02191690556004016020816003010492830192600103026138c8565b505b50613906929150613943565b5090565b828054828255905f5260205f209081019282156138fa579160200282015b828111156138fa578251825591602001919060010190613928565b5b80821115613906575f8155600101613944565b6001600160a01b03811681146113c5575f5ffd5b5f6020828403121561397b575f5ffd5b8135612bd981613957565b5f60208284031215613996575f5ffd5b5035919050565b5f5f83601f8401126139ad575f5ffd5b5081356001600160401b038111156139c3575f5ffd5b6020830191508360208285010111156139da575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b0312156139f8575f5ffd5b8835975060208901356001600160401b03811115613a14575f5ffd5b613a208b828c0161399d565b9098509650506040890135945060608901356001600160401b03811115613a45575f5ffd5b613a518b828c0161399d565b90955093505060808901356001600160401b03811115613a6f575f5ffd5b613a7b8b828c0161399d565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613ac85781516001600160a01b0316865260209586019590910190600101613aa1565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613ac8578151865260209586019590910190600101613ae4565b604081525f613b146040830185613a8f565b8281036020840152613b268185613ad2565b95945050505050565b5f5f5f60808486031215613b41575f5ffd5b833592506020840135915060808401851015613b5b575f5ffd5b6040840190509250925092565b5f5f60408385031215613b79575f5ffd5b823591506020830135613b8b81613957565b809150509250929050565b5f5f5f60608486031215613ba8575f5ffd5b833592506020840135613bba81613957565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612bd96020830184613a8f565b606081525f613c036060830186613ad2565b8281036020840152613c158186613ad2565b90508281036040840152613c298185613ad2565b9695505050505050565b5f5f60408385031215613c44575f5ffd5b8235613c4f81613957565b946020939093013593505050565b5f5f60408385031215613c6e575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613c9f57613c9f613c7d565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613cf157613cf1613cba565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613d1f57613d1f613cba565b604052919050565b805160048110611d56575f5ffd5b5f82601f830112613d44575f5ffd5b604080519081016001600160401b0381118282101715613d6657613d66613cba565b8060405250806040840185811115613d7c575f5ffd5b845b81811015613d96578051835260209283019201613d7e565b509195945050505050565b8051611d5681613957565b805160ff81168114611d56575f5ffd5b5f82601f830112613dcb575f5ffd5b81516001600160401b03811115613de457613de4613cba565b613df7601f8201601f1916602001613cf7565b818152846020838601011115613e0b575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611d56575f5ffd5b5f60208284031215613e46575f5ffd5b81516001600160401b03811115613e5b575f5ffd5b82016102008185031215613e6d575f5ffd5b613e75613cce565b81518152613e8560208301613d27565b602082015260408281015190820152613ea18560608401613d35565b606082015260a08201516080820152613ebc60c08301613da1565b60a0820152613ecd60e08301613dac565b60c08201526101008201516001600160401b03811115613eeb575f5ffd5b613ef786828501613dbc565b60e083015250613f0a6101208301613da1565b610100820152613f1d6101408301613da1565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613f53575f5ffd5b613f5f86828501613dbc565b61018083015250613f736101c08301613da1565b6101a0820152613f866101e08301613e27565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613ac85781546001600160a01b0316865260209095019460019182019101613fa9565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f61400a6080830189613f94565b828103602084015261401d81888a613fd0565b90508560408401528281036060840152614038818587613fd0565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161406a5761406a613c7d565b5060010190565b5f60208284031215614081575f5ffd5b5051919050565b803563ffffffff81168114611d56575f5ffd5b5f602082840312156140ab575f5ffd5b612bd982614088565b80820180821115610f8157610f81613c7d565b84815260a0810160208201855f5b60028110156141025763ffffffff6140ec83614088565b16835260209283019291909101906001016140d5565b50505060608201939093526080015292915050565b5f60208284031215614127575f5ffd5b612bd982613e27565b604081525f613b146040830185613f94565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f8157610f81613c7d565b64ffffffffff8181168382160190811115610f8157610f81613c7d565b6040810181835f5b60028110156141e95781518352602092830192909101906001016141ca565b50505092915050565b64ffffffffff8281168282160390811115610f8157610f81613c7d565b8082028115828204841417610f8157610f81613c7d565b848152836020820152606060408201525f613c29606083018486613fd0565b60018060a01b038816815286602082015285604082015260a060608201525f61427260a083018688613fd0565b8281036080840152614285818587613fd0565b9a9950505050505050505050565b5f6001600160401b038211156142ab576142ab613cba565b5060051b60200190565b5f82601f8301126142c4575f5ffd5b81516142d76142d282614293565b613cf7565b8082825260208201915060208360051b8601019250858311156142f8575f5ffd5b602085015b838110156143155780518352602092830192016142fd565b5095945050505050565b5f5f5f60608486031215614331575f5ffd5b83516001600160401b03811115614346575f5ffd5b8401601f81018613614356575f5ffd5b80516143646142d282614293565b8082825260208201915060208360051b850101925088831115614385575f5ffd5b6020840193505b828410156143a757835182526020938401939091019061438c565b8096505050505060208401516001600160401b038111156143c6575f5ffd5b6143d2868287016142b5565b92505060408401516001600160401b038111156143ed575f5ffd5b6143f9868287016142b5565b9150509250925092565b81810381811115610f8157610f81613c7d565b5f8261443057634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156112825780850481111561449757614497613c7d565b60018416156144a557908102905b60019390931c92800261447c565b5f826144c157506001610f81565b816144cd57505f610f81565b81600181146144e357600281146144ed5761451f565b6001915050610f81565b60ff8411156144fe576144fe613c7d565b6001841b915064ffffffffff82111561451957614519613c7d565b50610f81565b5060208310610133831016604e8410600b8410161715614557575081810a64ffffffffff81111561455257614552613c7d565b610f81565b61456764ffffffffff8484614478565b8064ffffffffff0482111561457e5761457e613c7d565b029392505050565b5f612bd964ffffffffff841664ffffffffff84166144b3565b64ffffffffff81811683821602908116908181146145bf576145bf613c7d565b509291505056fea164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 8132 + "start": 8271 }, { "length": 20, - "start": 8316 + "start": 8455 }, { "length": 20, - "start": 9716 + "start": 9855 }, { "length": 20, - "start": 13844 + "start": 13983 }, { "length": 20, - "start": 14286 + "start": 14425 } ] } @@ -1430,28 +1470,28 @@ "PoseidonT3": [ { "length": 20, - "start": 7918 + "start": 8057 }, { "length": 20, - "start": 8102 + "start": 8241 }, { "length": 20, - "start": 9502 + "start": 9641 }, { "length": 20, - "start": 13630 + "start": 13769 }, { "length": 20, - "start": 14072 + "start": 14211 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" + "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index 824ec22f6..71b316260 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -235,6 +235,9 @@ interface ICiphernodeRegistry { /// @notice Attestation count does not match bindings or proof honest-set size error AttestationBindingCountMismatch(); + /// @notice `partyId` is out of bounds for the finalized committee's `topNodes` + error PartyIdOutOfBounds(uint256 partyId, uint256 committeeSize); + /// @notice Node has already submitted a ticket for this E3 error NodeAlreadySubmitted(); @@ -477,6 +480,20 @@ interface ICiphernodeRegistry { address node ) external view returns (bool); + /// @notice Return the operator address at slot `partyId` in the finalized + /// committee's `topNodes` (address-ascending order). + /// @dev Unlike `getCommitteeNodes`, this does NOT require the committee to + /// be published yet — it works as soon as the committee is finalized, + /// which is what `publishCommittee` callers need in order to bind a + /// `partyId` to a specific operator before the public key is stored. + /// @param e3Id ID of the E3 computation + /// @param partyId Index into `topNodes` + /// @return Operator address at `topNodes[partyId]` + function getCommitteeNodeAt( + uint256 e3Id, + uint256 partyId + ) external view returns (address); + /// @notice Get active (non-expelled) committee nodes for an E3 /// @param e3Id ID of the E3 computation /// @return nodes Array of active committee member addresses diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 342ddd33b..5b56f93c1 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -646,6 +646,16 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { ICiphernodeRegistry.MemberStatus.None; } + /// @inheritdoc ICiphernodeRegistry + function getCommitteeNodeAt( + uint256 e3Id, + uint256 partyId + ) external view returns (address) { + address[] storage top = committees[e3Id].topNodes; + require(partyId < top.length, PartyIdOutOfBounds(partyId, top.length)); + return top[partyId]; + } + /// @inheritdoc ICiphernodeRegistry function getActiveCommitteeNodes( uint256 e3Id diff --git a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol index 949fab5aa..b5068af4f 100644 --- a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol +++ b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol @@ -238,11 +238,14 @@ contract SlashingManager is ISlashingManager, AccessControl { /// to pass a reason and preventing cross-reason replay attacks. /// Evidence format: /// `abi.encode(uint256 proofType, - /// address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures)` + /// address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures, + /// bytes evidence)` /// Each voter must sign via `personal_sign`/`signMessage()` (EIP-191 prefixed): /// `personal_sign(keccak256(abi.encode(VOTE_TYPEHASH, /// block.chainid, e3Id, accusationId, voter, agrees, dataHash)))` /// where `accusationId = keccak256(abi.encodePacked(block.chainid, e3Id, operator, proofType))` + /// and `evidence` is the preimage of every voter's `dataHash`, i.e. + /// `keccak256(evidence) == dataHashes[i]` for all i. function proposeSlash( uint256 e3Id, address operator, @@ -398,8 +401,12 @@ contract SlashingManager is ISlashingManager, AccessControl { address[] memory voters, bool[] memory agrees, bytes32[] memory dataHashes, - bytes[] memory signatures - ) = abi.decode(proof, (uint256, address[], bool[], bytes32[], bytes[])); + bytes[] memory signatures, + bytes memory evidence + ) = abi.decode( + proof, + (uint256, address[], bool[], bytes32[], bytes[], bytes) + ); uint256 numVotes = voters.length; require( @@ -409,12 +416,10 @@ contract SlashingManager is ISlashingManager, AccessControl { InvalidProof() ); - // Compute accusation ID matching AccusationManager::accusation_id() on the Rust side - bytes32 accusationId = keccak256( - abi.encodePacked(block.chainid, e3Id, operator, proofType) - ); - - // Get committee threshold — need at least M agreeing votes + // Get committee threshold — need at least M agreeing votes. + // Checked before the evidence binding so cardinality errors surface + // as `InsufficientAttestations` rather than masquerading as `InvalidProof`. + require(numVotes > 0, InsufficientAttestations()); { (, uint32 thresholdM, , ) = ciphernodeRegistry .getCommitteeViability(e3Id); @@ -422,48 +427,84 @@ contract SlashingManager is ISlashingManager, AccessControl { require(numVotes >= thresholdM, InsufficientAttestations()); } - // Verify each vote signature and membership + // All voters must attest to the *same* evidence, and that evidence + // must be cryptographically bound to actual on-chain bytes: + // 1. dataHashes[i] all equal a single `commonDataHash`. + // 2. `keccak256(evidence) == commonDataHash`. + // + // Together these close the attribution chain: voters don't just say + // "I saw something bad", they cryptographically agree on the exact + // bytes — which the contract can inspect and which any third party + // can audit from the transaction calldata. + bytes32 commonDataHash = dataHashes[0]; + require(keccak256(evidence) == commonDataHash, InvalidProof()); + + // Verify each vote signature and membership. Computed inside a helper + // to keep the outer frame's local-variable count under the EVM stack + // limit (16 slots). + _verifyVotes( + VoteVerifyInputs({ + e3Id: e3Id, + operator: operator, + proofType: proofType, + commonDataHash: commonDataHash, + voters: voters, + agrees: agrees, + dataHashes: dataHashes, + signatures: signatures + }) + ); + } + + struct VoteVerifyInputs { + uint256 e3Id; + address operator; + uint256 proofType; + bytes32 commonDataHash; + address[] voters; + bool[] agrees; + bytes32[] dataHashes; + bytes[] signatures; + } + + function _verifyVotes(VoteVerifyInputs memory v) private view { + bytes32 accusationId = keccak256( + abi.encodePacked(block.chainid, v.e3Id, v.operator, v.proofType) + ); + address prevVoter = address(0); + uint256 numVotes = v.voters.length; for (uint256 i = 0; i < numVotes; i++) { - address voter = voters[i]; + address voter = v.voters[i]; - // Sorted ascending order prevents duplicate voters require(voter > prevVoter, DuplicateVoter()); prevVoter = voter; - // The accused cannot vote on their own accusation (conflict of interest) - require(voter != operator, VoterIsAccused()); - - // All votes must agree the proof is bad (fault confirmed) - require(agrees[i], InvalidProof()); - - // Verify voter is an active committee member for this E3 + require(voter != v.operator, VoterIsAccused()); + require(v.agrees[i], InvalidProof()); + require(v.dataHashes[i] == v.commonDataHash, InvalidProof()); require( - ciphernodeRegistry.isCommitteeMemberActive(e3Id, voter), + ciphernodeRegistry.isCommitteeMemberActive(v.e3Id, voter), VoterNotInCommittee() ); - // Reconstruct vote digest and verify signature in a scoped block - // to avoid stack-too-deep - { - bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash( - keccak256( - abi.encode( - VOTE_TYPEHASH, - block.chainid, - e3Id, - accusationId, - voter, - agrees[i], - dataHashes[i] - ) + bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash( + keccak256( + abi.encode( + VOTE_TYPEHASH, + block.chainid, + v.e3Id, + accusationId, + voter, + v.agrees[i], + v.dataHashes[i] ) - ); - require( - ECDSA.recover(ethSignedHash, signatures[i]) == voter, - InvalidVoteSignature() - ); - } + ) + ); + require( + ECDSA.recover(ethSignedHash, v.signatures[i]) == voter, + InvalidVoteSignature() + ); } } diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index fbbce3fee..b6f40b927 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -178,6 +178,18 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { return false; } + function getCommitteeNodeAt( + uint256 e3Id, + uint256 partyId + ) external view returns (address) { + address[] storage nodes = _committeeNodes[e3Id]; + require( + partyId < nodes.length, + PartyIdOutOfBounds(partyId, nodes.length) + ); + return nodes[partyId]; + } + function getActiveCommitteeNodes( uint256 ) external pure returns (address[] memory nodes, uint256[] memory scores) { @@ -319,6 +331,13 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { return false; } + function getCommitteeNodeAt( + uint256, + uint256 + ) external pure returns (address) { + return address(0); + } + function getActiveCommitteeNodes( uint256 ) external pure returns (address[] memory nodes, uint256[] memory scores) { diff --git a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol index 1760af153..45b55ed77 100644 --- a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol @@ -144,6 +144,20 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { BundleData memory data, DkgFoldAttestationLib.PartySlotBinding memory binding ) private view returns (uint256 slot, bytes32 skCommit, bytes32 esmCommit) { + // Structural check: the binding's `node` must be the operator + // registered at `topNodes[partyId]` on chain. This prevents an + // aggregator from reassigning a node's signed attestation to a + // different slot (e.g. claiming the operator at `topNodes[0]` is + // party 1) even if the operator cooperated by signing with the + // wrong `partyId`. Combined with the `ecrecover` check below, the + // attestation is bound to *both* the right address and the right slot. + require( + ICiphernodeRegistry(registry).getCommitteeNodeAt( + e3Id, + binding.partyId + ) == binding.node, + ICiphernodeRegistry.InvalidFoldAttestation() + ); require( ICiphernodeRegistry(registry).isCommitteeMemberActive( e3Id, diff --git a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index d3f1897fa..50f2c08c3 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -402,13 +402,9 @@ describe("BfvVkBindingIntegration", function () { dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.lo], ); - expect( - await bfvPk.verify.staticCall( - pkCommitment, - dkgCommitteeHash, - dkgEncoded, - ), - ).to.equal(false); + await expect( + bfvPk.verify.staticCall(pkCommitment, dkgCommitteeHash, dkgEncoded), + ).to.be.revertedWithCustomError(bfvPk, "BadNodesFoldKeyHash"); }, ); }); diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index f7fcbc379..3b11f17c7 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -142,6 +142,19 @@ describe("Enclave", function () { esmCommits, ); + // `topNodes` is address-ascending on chain; bindings must map + // `partyId -> topNodes[partyId]`, so sort operators by address first. + const operatorWithAddrs = await Promise.all( + operators.map(async (op) => ({ op, addr: await op.getAddress() })), + ); + operatorWithAddrs.sort((a, b) => + a.addr.toLowerCase() < b.addr.toLowerCase() + ? -1 + : a.addr.toLowerCase() > b.addr.toLowerCase() + ? 1 + : 0, + ); + const { chainId } = await ethers.provider.getNetwork(); const attestations: { partyId: number; @@ -152,8 +165,8 @@ describe("Enclave", function () { const bindings: { partyId: number; node: string }[] = []; for (let i = 0; i < h; i++) { - const operator = operators[i]!; - const node = await operator.getAddress(); + const operator = operatorWithAddrs[i]!.op; + const node = operatorWithAddrs[i]!.addr; const partyId = partyIds[i]!; attestations.push({ partyId, diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index 177cad5bd..88fcb3c3f 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -523,13 +523,18 @@ describe("Committee Expulsion & Fault Tolerance", function () { ]); // Lane A: Slash op1 with attestation from [op2, op3] — active 3→2, still >= M=2 + // Evidence is the preimage of dataHash; the contract enforces + // `keccak256(evidence) == dataHash` and equal dataHashes across voters. + const evidence1 = ethers.hexlify(ethers.toUtf8Bytes("data1")); const proof = await signAndEncodeAttestation( [operator2, operator3], 0, await operator1.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("data1")), + ethers.keccak256(evidence1), + undefined, + evidence1, ); await slashingManager.proposeSlash( 0, @@ -592,13 +597,16 @@ describe("Committee Expulsion & Fault Tolerance", function () { ]); // Slash operator1 once + const ev1 = ethers.hexlify(ethers.toUtf8Bytes("first")); const proof1 = await signAndEncodeAttestation( [operator2, operator3], 0, await operator1.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("first")), + ethers.keccak256(ev1), + undefined, + ev1, ); await slashingManager.proposeSlash( 0, @@ -610,13 +618,16 @@ describe("Committee Expulsion & Fault Tolerance", function () { // Slash operator1 again for a different proof type to verify expulsion is idempotent. // Same (e3Id, operator, proofType) would revert DuplicateEvidence — that's correct. // Using proofType=7 (C6ThresholdShareDecryption) with REASON_PT_7 instead. + const ev2 = ethers.hexlify(ethers.toUtf8Bytes("second")); const proof2 = await signAndEncodeAttestation( [operator2, operator3], 0, await operator1.getAddress(), 7, // C6ThresholdShareDecryption — different proofType 31337, - ethers.keccak256(ethers.toUtf8Bytes("second")), + ethers.keccak256(ev2), + undefined, + ev2, ); await slashingManager.proposeSlash( 0, @@ -724,13 +735,16 @@ describe("Committee Expulsion & Fault Tolerance", function () { expect((await registry.getCommitteeViability(0)).activeCount).to.equal(4); // Expel 2 out of 4 — still have 2 >= M=2 + const evExpel1 = ethers.hexlify(ethers.toUtf8Bytes("expel1")); const proof1 = await signAndEncodeAttestation( [operator2, operator3], 0, await operator1.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel1")), + ethers.keccak256(evExpel1), + undefined, + evExpel1, ); await slashingManager.proposeSlash( 0, @@ -739,13 +753,16 @@ describe("Committee Expulsion & Fault Tolerance", function () { ); expect((await registry.getCommitteeViability(0)).activeCount).to.equal(3); + const evExpel2 = ethers.hexlify(ethers.toUtf8Bytes("expel2")); const proof2 = await signAndEncodeAttestation( [operator3, operator4], 0, await operator2.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel2")), + ethers.keccak256(evExpel2), + undefined, + evExpel2, ); await slashingManager.proposeSlash( 0, @@ -869,13 +886,16 @@ describe("Committee Expulsion & Fault Tolerance", function () { ]); // Expel operator1 — still viable (3 >= 2) + const evExpelOp1 = ethers.hexlify(ethers.toUtf8Bytes("expel-op1")); const proof1 = await signAndEncodeAttestation( [operator2, operator3], 0, await operator1.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel-op1")), + ethers.keccak256(evExpelOp1), + undefined, + evExpelOp1, ); await slashingManager.proposeSlash( 0, @@ -884,13 +904,16 @@ describe("Committee Expulsion & Fault Tolerance", function () { ); // Expel operator2 — still viable (2 >= 2) + const evExpelOp2 = ethers.hexlify(ethers.toUtf8Bytes("expel-op2")); const proof2 = await signAndEncodeAttestation( [operator3, operator4], 0, await operator2.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel-op2")), + ethers.keccak256(evExpelOp2), + undefined, + evExpelOp2, ); await slashingManager.proposeSlash( 0, diff --git a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts index 8a051fb92..54c840303 100644 --- a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts +++ b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts @@ -692,11 +692,15 @@ describe("SlashingManager", function () { const dataHashes: string[] = []; const signatures: string[] = []; + // `dataHash` must equal `keccak256(evidence)` per the on-chain check. + const evidence = "0x"; + const evidenceHash = ethers.keccak256(evidence); + for (let i = 0; i < sortedVoters.length; i++) { const voterAddr = sortedVoters[i]; voters.push(voterAddr); agrees.push(true); - dataHashes.push(ethers.ZeroHash); + dataHashes.push(evidenceHash); // For the second voter, use notTheOwner to sign (wrong signer) const signerToUse = @@ -719,7 +723,7 @@ describe("SlashingManager", function () { accusationId, voterAddr, true, - ethers.ZeroHash, + evidenceHash, ], ), ); @@ -730,8 +734,8 @@ describe("SlashingManager", function () { } const proof = abiCoder.encode( - ["uint256", "address[]", "bool[]", "bytes32[]", "bytes[]"], - [0, voters, agrees, dataHashes, signatures], + ["uint256", "address[]", "bool[]", "bytes32[]", "bytes[]", "bytes"], + [0, voters, agrees, dataHashes, signatures, evidence], ); await expect( diff --git a/packages/enclave-contracts/test/fixtures/attestation.ts b/packages/enclave-contracts/test/fixtures/attestation.ts index ed3702670..93327c966 100644 --- a/packages/enclave-contracts/test/fixtures/attestation.ts +++ b/packages/enclave-contracts/test/fixtures/attestation.ts @@ -20,8 +20,14 @@ export const VOTE_TYPEHASH = ethers.keccak256( /** * Helper to create signed committee attestation evidence for Lane A. * Each voter signs a VOTE_TYPEHASH-structured digest via personal_sign (EIP-191). - * Returns abi.encode(proofType, voters, agrees, dataHashes, signatures) + * Returns + * abi.encode(proofType, voters, agrees, dataHashes, signatures, evidence) * with voters sorted ascending by address. + * + * `evidence` is the preimage of `dataHash` (the SlashingManager contract enforces + * `keccak256(evidence) == dataHash`). If `evidence` is provided, `dataHash` is + * derived from it automatically; pass `dataHash` explicitly only to test the + * keccak-binding check itself. */ export async function signAndEncodeAttestation( voterSigners: Signer[], @@ -29,9 +35,13 @@ export async function signAndEncodeAttestation( operator: string, proofType: number = 0, chainId: number = 31337, - dataHash: string = ethers.ZeroHash, + dataHash?: string, agreesOverride?: boolean[], + evidence: string = "0x", ): Promise { + if (dataHash === undefined) { + dataHash = ethers.keccak256(evidence); + } const accusationId = ethers.keccak256( ethers.solidityPacked( ["uint256", "uint256", "address", "uint256"], @@ -99,7 +109,7 @@ export async function signAndEncodeAttestation( } return abiCoder.encode( - ["uint256", "address[]", "bool[]", "bytes32[]", "bytes[]"], - [proofType, voters, agrees, dataHashes, signatures], + ["uint256", "address[]", "bool[]", "bytes32[]", "bytes[]", "bytes"], + [proofType, voters, agrees, dataHashes, signatures, evidence], ); } From 17cdddc0e5e0987d43e6fd9cea07ec4640406db8 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 11:16:44 +0200 Subject: [PATCH 28/54] switch to canonical EIP-712, owner mutable verifier and immediate proposeSlash --- .../src/ciphernode_builder.rs | 16 +- crates/config/src/contract.rs | 5 + .../src/enclave_event/dkg_fold_attestation.rs | 70 ++++++- crates/evm/src/dkg_attestation_bundle.rs | 3 +- crates/zk-prover/src/actors/mod.rs | 16 +- .../src/actors/node_proof_aggregator.rs | 101 +++++---- .../contracts/Enclave.sol/Enclave.json | 2 +- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 89 +++++++- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../ISlashingManager.json | 2 +- .../CiphernodeRegistryOwnable.json | 197 ++++++++++++++++-- .../interfaces/ICiphernodeRegistry.sol | 29 +++ .../contracts/lib/DkgFoldAttestationLib.sol | 88 ++++++-- .../registry/CiphernodeRegistryOwnable.sol | 73 ++++++- .../contracts/slashing/SlashingManager.sol | 51 +++-- .../verifiers/DkgFoldAttestationVerifier.sol | 1 + .../scripts/deployEnclave.ts | 2 +- .../enclave-contracts/test/Enclave.spec.ts | 42 ++-- .../CiphernodeRegistryOwnable.spec.ts | 2 +- .../test/Slashing/SlashingManager.spec.ts | 12 +- tests/integration/enclave.config.yaml | 3 + 22 files changed, 667 insertions(+), 141 deletions(-) diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 8c0e0bbbb..fc2280b0f 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -486,7 +486,13 @@ impl CiphernodeBuilder { )); info!("Setting up ZK actors"); - setup_zk_actors(&bus, backend, signer); + let dkg_fold_verifier_addr = self + .chains + .first() + .and_then(|c| c.contracts.dkg_fold_attestation_verifier.as_ref()) + .map(|c| c.address()) + .transpose()?; + setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_addr); } if self.pubkey_agg { @@ -507,7 +513,13 @@ impl CiphernodeBuilder { .ok_or_else(|| anyhow::anyhow!("ZK backend is required for aggregator"))?; let signer = provider_cache.ensure_signer().await?; info!("Setting up ZK actors for aggregator"); - setup_zk_actors(&bus, backend, signer); + let dkg_fold_verifier_addr = self + .chains + .first() + .and_then(|c| c.contracts.dkg_fold_attestation_verifier.as_ref()) + .map(|c| c.address()) + .transpose()?; + setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_addr); } } diff --git a/crates/config/src/contract.rs b/crates/config/src/contract.rs index 32c856d2a..7af3277db 100644 --- a/crates/config/src/contract.rs +++ b/crates/config/src/contract.rs @@ -49,6 +49,10 @@ pub struct ContractAddresses { pub e3_program: Option, pub fee_token: Option, pub slashing_manager: Option, + /// On-chain `DkgFoldAttestationVerifier` address. Required when running with + /// proof aggregation; used as the EIP-712 `verifyingContract` when nodes + /// sign DKG fold attestations. + pub dkg_fold_attestation_verifier: Option, } impl ContractAddresses { @@ -60,6 +64,7 @@ impl ContractAddresses { self.e3_program.as_ref(), self.fee_token.as_ref(), self.slashing_manager.as_ref(), + self.dkg_fold_attestation_verifier.as_ref(), ] .into_iter() .flatten() diff --git a/crates/events/src/enclave_event/dkg_fold_attestation.rs b/crates/events/src/enclave_event/dkg_fold_attestation.rs index 9fcaf3f6f..a6d94644f 100644 --- a/crates/events/src/enclave_event/dkg_fold_attestation.rs +++ b/crates/events/src/enclave_event/dkg_fold_attestation.rs @@ -26,25 +26,64 @@ pub struct DkgFoldAggCommits { pub esm_agg_commit: [u8; 32], } -/// Payload signed after `NodeDkgFold` completes. +/// Canonical EIP-712 payload signed after `NodeDkgFold` completes. +/// +/// `chainId` and `verifying_contract` (the `DkgFoldAttestationVerifier`) are +/// part of the EIP-712 domain; `e3_id`, `party_id`, and the commitments are the +/// struct fields. Must stay aligned with `DkgFoldAttestationLib` in +/// `packages/enclave-contracts`. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct DkgFoldAttestationPayload { pub e3_id: E3id, + /// Address of the on-chain `DkgFoldAttestationVerifier` (EIP-712 `verifyingContract`). + pub verifying_contract: Address, /// Sortition / committee slot id (index into on-chain `topNodes` when ids are dense). pub party_id: u64, pub agg_commits: DkgFoldAggCommits, } impl DkgFoldAttestationPayload { - /// Must match `DKG_FOLD_ATTESTATION_TYPEHASH` in `CiphernodeRegistryOwnable.sol` when added. + /// `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. + pub fn domain_typehash() -> [u8; 32] { + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", + ) + .into() + } + + /// `keccak256("EnclaveDkgFoldAttestation")`. + pub fn domain_name_hash() -> [u8; 32] { + keccak256("EnclaveDkgFoldAttestation").into() + } + + /// `keccak256("1")`. + pub fn domain_version_hash() -> [u8; 32] { + keccak256("1").into() + } + + /// `keccak256("DkgFoldAttestation(uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)")`. pub fn typehash() -> [u8; 32] { keccak256( - "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", + "DkgFoldAttestation(uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", ) .into() } - pub fn digest(&self) -> Result<[u8; 32]> { + /// EIP-712 domain separator for this payload's chain + verifier. + pub fn domain_separator(&self) -> [u8; 32] { + let encoded = ( + Self::domain_typehash(), + Self::domain_name_hash(), + Self::domain_version_hash(), + U256::from(self.e3_id.chain_id()), + self.verifying_contract, + ) + .abi_encode(); + keccak256(&encoded).into() + } + + /// EIP-712 `hashStruct(DkgFoldAttestation)`. + pub fn struct_hash(&self) -> Result<[u8; 32]> { let e3_id_u256: U256 = self .e3_id .clone() @@ -52,7 +91,6 @@ impl DkgFoldAttestationPayload { .map_err(|_| anyhow!("E3id cannot be converted to U256"))?; let encoded = ( Self::typehash(), - U256::from(self.e3_id.chain_id()), e3_id_u256, U256::from(self.party_id), self.agg_commits.sk_agg_commit, @@ -61,9 +99,21 @@ impl DkgFoldAttestationPayload { .abi_encode(); Ok(keccak256(&encoded).into()) } + + /// EIP-712 typed-data hash: `keccak256("\x19\x01" || domainSeparator || structHash)`. + pub fn digest(&self) -> Result<[u8; 32]> { + let domain = self.domain_separator(); + let struct_hash = self.struct_hash()?; + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(&domain); + buf.extend_from_slice(&struct_hash); + Ok(keccak256(&buf).into()) + } } -/// `eth_sign` over [`DkgFoldAttestationPayload::digest`]. +/// EIP-712 typed-data signature over [`DkgFoldAttestationPayload::digest`]. #[derive(Derivative, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derivative(Debug)] pub struct SignedDkgFoldAttestation { @@ -75,8 +125,11 @@ pub struct SignedDkgFoldAttestation { impl SignedDkgFoldAttestation { pub fn sign(payload: DkgFoldAttestationPayload, signer: &PrivateKeySigner) -> Result { let digest = payload.digest()?; + // `sign_hash_sync` signs the raw 32-byte hash without EIP-191 + // wrapping, which is what EIP-712 requires (`digest` is already + // the `\x19\x01 || domainSeparator || structHash` hash). let sig = signer - .sign_message_sync(&digest) + .sign_hash_sync(&digest.into()) .map_err(|e| anyhow!("Failed to sign DkgFoldAttestation: {e}"))?; Ok(Self { payload, @@ -89,7 +142,7 @@ impl SignedDkgFoldAttestation { let sig = Signature::try_from(&self.signature[..]) .map_err(|e| anyhow!("Invalid DkgFoldAttestation signature: {e}"))?; let digest = self.payload.digest()?; - sig.recover_address_from_msg(&digest) + sig.recover_address_from_prehash(&digest.into()) .map_err(|e| anyhow!("Failed to recover DkgFoldAttestation signer: {e}")) } @@ -111,6 +164,7 @@ mod tests { .unwrap(); let payload = DkgFoldAttestationPayload { e3_id: E3id::new("0", 1), + verifying_contract: Address::from([0x11u8; 20]), party_id: 1, agg_commits: DkgFoldAggCommits { sk_agg_commit: [7u8; 32], diff --git a/crates/evm/src/dkg_attestation_bundle.rs b/crates/evm/src/dkg_attestation_bundle.rs index aff196321..43c849069 100644 --- a/crates/evm/src/dkg_attestation_bundle.rs +++ b/crates/evm/src/dkg_attestation_bundle.rs @@ -90,6 +90,7 @@ mod tests { .unwrap(); let payload = DkgFoldAttestationPayload { e3_id: E3id::new("31337", 1), + verifying_contract: Address::from([0x22u8; 20]), party_id: 2, agg_commits: DkgFoldAggCommits { sk_agg_commit: [1u8; 32], @@ -109,7 +110,7 @@ mod tests { assert!(!encoded.is_empty()); let typehash = keccak256( - "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", + "DkgFoldAttestation(uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", ); assert_eq!(typehash.len(), 32); } diff --git a/crates/zk-prover/src/actors/mod.rs b/crates/zk-prover/src/actors/mod.rs index 914f0b17b..026eccc9a 100644 --- a/crates/zk-prover/src/actors/mod.rs +++ b/crates/zk-prover/src/actors/mod.rs @@ -30,7 +30,7 @@ //! let signer = PrivateKeySigner::random(); //! //! // Setup all actors with proper separation of concerns -//! setup_zk_actors(&bus, &backend, signer); +//! setup_zk_actors(&bus, &backend, signer, None); //! ``` pub mod accusation_manager; @@ -56,6 +56,7 @@ pub use share_verification::ShareVerificationActor; pub use zk_actor::ZkActor; use actix::{Actor, Addr}; +use alloy::primitives::Address; use alloy::signers::local::PrivateKeySigner; use e3_events::BusHandle; @@ -65,14 +66,23 @@ use crate::ZkBackend; /// /// Requires a `ZkBackend` for proof generation/verification and a /// `PrivateKeySigner` for signing proofs (fault attribution). -pub fn setup_zk_actors(bus: &BusHandle, backend: &ZkBackend, signer: PrivateKeySigner) -> ZkActors { +/// `dkg_fold_attestation_verifier` is the on-chain verifier address used as +/// the EIP-712 `verifyingContract` for fold attestations; required when +/// proof aggregation is enabled. +pub fn setup_zk_actors( + bus: &BusHandle, + backend: &ZkBackend, + signer: PrivateKeySigner, + dkg_fold_attestation_verifier: Option
, +) -> ZkActors { let zk_actor = ZkActor::new(backend).start(); let verifier = zk_actor.clone().recipient(); let proof_request = ProofRequestActor::setup(bus, signer.clone()); let proof_verification = ProofVerificationActor::setup(bus, verifier); let share_verification = ShareVerificationActor::setup(bus); - let node_proof_aggregator = NodeProofAggregator::setup(bus, signer); + let node_proof_aggregator = + NodeProofAggregator::setup(bus, signer, dkg_fold_attestation_verifier); ZkActors { zk_actor, diff --git a/crates/zk-prover/src/actors/node_proof_aggregator.rs b/crates/zk-prover/src/actors/node_proof_aggregator.rs index 03092e98d..971db6431 100644 --- a/crates/zk-prover/src/actors/node_proof_aggregator.rs +++ b/crates/zk-prover/src/actors/node_proof_aggregator.rs @@ -10,6 +10,7 @@ use std::collections::{BTreeMap, HashMap}; use actix::{Actor, Addr, Context, Handler}; +use alloy::primitives::Address; use alloy::signers::local::PrivateKeySigner; use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeResponse, ComputeResponseKind, @@ -48,24 +49,36 @@ struct DkgProofCollectionState { pub struct NodeProofAggregator { bus: BusHandle, signer: PrivateKeySigner, + /// On-chain `DkgFoldAttestationVerifier` address (EIP-712 `verifyingContract`). + /// `None` is only valid when proof aggregation will never run for this node. + dkg_fold_attestation_verifier: Option
, states: HashMap, fold_correlation: HashMap, pending_inner_proofs: HashMap>, } impl NodeProofAggregator { - pub fn new(bus: &BusHandle, signer: PrivateKeySigner) -> Self { + pub fn new( + bus: &BusHandle, + signer: PrivateKeySigner, + dkg_fold_attestation_verifier: Option
, + ) -> Self { Self { bus: bus.clone(), signer, + dkg_fold_attestation_verifier, states: HashMap::new(), fold_correlation: HashMap::new(), pending_inner_proofs: HashMap::new(), } } - pub fn setup(bus: &BusHandle, signer: PrivateKeySigner) -> Addr { - let addr = Self::new(bus, signer).start(); + pub fn setup( + bus: &BusHandle, + signer: PrivateKeySigner, + dkg_fold_attestation_verifier: Option
, + ) -> Addr { + let addr = Self::new(bus, signer, dkg_fold_attestation_verifier).start(); bus.subscribe(EventType::ThresholdSharePending, addr.clone().into()); bus.subscribe(EventType::DKGInnerProofReady, addr.clone().into()); bus.subscribe(EventType::ComputeResponse, addr.clone().into()); @@ -315,47 +328,59 @@ impl NodeProofAggregator { let committee_h = committee_n; let n_moduli = state.meta.n_moduli; - let fold_attestation = - match extract_node_fold_agg_commits(&proof, committee_n, committee_h, n_moduli) { - Ok((extracted_party, commits)) => { - if extracted_party != party_id { - error!( - e3_id = %e3_id, - expected_party_id = party_id, - extracted_party_id = extracted_party, - "NodeFold public party_id does not match sortition party_id" - ); - None - } else { - let payload = DkgFoldAttestationPayload { - e3_id: e3_id.clone(), - party_id, - agg_commits: commits, - }; - match SignedDkgFoldAttestation::sign(payload, &self.signer) { - Ok(signed) => Some(signed), - Err(e) => { - error!( - e3_id = %e3_id, - party_id, - error = %e, - "failed to sign DkgFoldAttestation" - ); - None - } + let fold_attestation = match extract_node_fold_agg_commits( + &proof, + committee_n, + committee_h, + n_moduli, + ) { + Ok((extracted_party, commits)) => { + if extracted_party != party_id { + error!( + e3_id = %e3_id, + expected_party_id = party_id, + extracted_party_id = extracted_party, + "NodeFold public party_id does not match sortition party_id" + ); + None + } else if let Some(verifying_contract) = self.dkg_fold_attestation_verifier { + let payload = DkgFoldAttestationPayload { + e3_id: e3_id.clone(), + verifying_contract, + party_id, + agg_commits: commits, + }; + match SignedDkgFoldAttestation::sign(payload, &self.signer) { + Ok(signed) => Some(signed), + Err(e) => { + error!( + e3_id = %e3_id, + party_id, + error = %e, + "failed to sign DkgFoldAttestation" + ); + None } } - } - Err(e) => { + } else { error!( e3_id = %e3_id, party_id, - error = %e, - "failed to extract sk_agg/esm_agg from NodeFold proof" + "NodeProofAggregator: cannot sign DkgFoldAttestation — `dkg_fold_attestation_verifier` address not configured" ); None } - }; + } + Err(e) => { + error!( + e3_id = %e3_id, + party_id, + error = %e, + "failed to extract sk_agg/esm_agg from NodeFold proof" + ); + None + } + }; if fold_attestation.is_none() { error!( @@ -556,7 +581,7 @@ mod tests { #[actix::test] async fn node_dkg_fold_compute_error_emits_none_aggregation_result() -> Result<()> { let (bus, _rng, _seed, _params, _crp, _errors, history) = get_common_setup(None)?; - let mut aggregator = NodeProofAggregator::new(&bus, test_signer()); + let mut aggregator = NodeProofAggregator::new(&bus, test_signer(), None); let e3_id = E3id::new("42", 1); let correlation_id = CorrelationId::new(); @@ -638,7 +663,7 @@ mod tests { #[actix::test] async fn early_inner_proof_is_prebuffered_until_collection_starts() -> Result<()> { let (bus, _rng, _seed, _params, _crp, _errors, history) = get_common_setup(None)?; - let mut aggregator = NodeProofAggregator::new(&bus, test_signer()); + let mut aggregator = NodeProofAggregator::new(&bus, test_signer(), None); let e3_id = E3id::new("43", 1); let early_proof = dummy_proof(10); diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 8621d44a4..d204daefb 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -2564,5 +2564,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" + "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 5496e9275..7dda1e846 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -1005,5 +1005,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" + "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index ef2a767b2..1b4330557 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -69,6 +69,11 @@ "name": "DkgProofRequired", "type": "error" }, + { + "inputs": [], + "name": "FoldAttestationVerifierAlreadySet", + "type": "error" + }, { "inputs": [], "name": "FoldAttestationVerifierNotSet", @@ -110,6 +115,11 @@ "name": "InvalidTicketNumber", "type": "error" }, + { + "inputs": [], + "name": "NoPendingVerifierUpdate", + "type": "error" + }, { "inputs": [], "name": "NodeAlreadySubmitted", @@ -202,6 +212,38 @@ "name": "Unauthorized", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "pending", + "type": "address" + }, + { + "internalType": "address", + "name": "provided", + "type": "address" + } + ], + "name": "VerifierMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nowAt", + "type": "uint256" + } + ], + "name": "VerifierUpdateTimelockActive", + "type": "error" + }, { "inputs": [], "name": "ZeroAddress", @@ -474,6 +516,51 @@ "name": "CommitteeViabilityUpdated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + } + ], + "name": "DkgFoldAttestationVerifierProposalCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + } + ], + "name": "DkgFoldAttestationVerifierProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + } + ], + "name": "DkgFoldAttestationVerifierUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -1099,5 +1186,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" + "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index deda36500..da9b477ee 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2097,5 +2097,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" + "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index 646225a73..9deb40fc9 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -1006,5 +1006,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" + "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index ad0651ef8..044982d45 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -74,6 +74,11 @@ "name": "DkgProofRequired", "type": "error" }, + { + "inputs": [], + "name": "FoldAttestationVerifierAlreadySet", + "type": "error" + }, { "inputs": [], "name": "FoldAttestationVerifierNotSet", @@ -120,6 +125,11 @@ "name": "InvalidTicketNumber", "type": "error" }, + { + "inputs": [], + "name": "NoPendingVerifierUpdate", + "type": "error" + }, { "inputs": [], "name": "NodeAlreadySubmitted", @@ -239,6 +249,38 @@ "name": "Unauthorized", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "pending", + "type": "address" + }, + { + "internalType": "address", + "name": "provided", + "type": "address" + } + ], + "name": "VerifierMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nowAt", + "type": "uint256" + } + ], + "name": "VerifierUpdateTimelockActive", + "type": "error" + }, { "inputs": [], "name": "ZeroAddress", @@ -524,6 +566,51 @@ "name": "CommitteeViabilityUpdated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + } + ], + "name": "DkgFoldAttestationVerifierProposalCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + } + ], + "name": "DkgFoldAttestationVerifierProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "verifier", + "type": "address" + } + ], + "name": "DkgFoldAttestationVerifierUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -626,6 +713,19 @@ "name": "TicketSubmitted", "type": "event" }, + { + "inputs": [], + "name": "DKG_FOLD_VERIFIER_TIMELOCK", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "TREE_DEPTH", @@ -665,6 +765,13 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "cancelDkgFoldAttestationVerifierProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -721,6 +828,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "contract IDkgFoldAttestationVerifier", + "name": "verifier", + "type": "address" + } + ], + "name": "commitDkgFoldAttestationVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1149,6 +1269,45 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "pendingDkgFoldAttestationVerifier", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingDkgFoldAttestationVerifierAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDkgFoldAttestationVerifier", + "name": "verifier", + "type": "address" + } + ], + "name": "proposeDkgFoldAttestationVerifier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1317,12 +1476,12 @@ { "inputs": [ { - "internalType": "contract IDkgFoldAttestationVerifier", - "name": "verifier", + "internalType": "contract IEnclave", + "name": "_enclave", "type": "address" } ], - "name": "setDkgFoldAttestationVerifier", + "name": "setEnclave", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1330,12 +1489,12 @@ { "inputs": [ { - "internalType": "contract IEnclave", - "name": "_enclave", + "internalType": "contract IDkgFoldAttestationVerifier", + "name": "verifier", "type": "address" } ], - "name": "setEnclave", + "name": "setInitialDkgFoldAttestationVerifier", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -1437,30 +1596,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6145d3806100d65f395ff3fe608060405234801561000f575f5ffd5b5060043610610276575f3560e01c80639a7a2ffc11610156578063dbb06c93116100ca578063f165053611610084578063f165053614610606578063f2fde38b14610620578063f379b0df14610633578063f52fd8031461066d578063f6fc05d5146106dd578063fa932569146106e6575f5ffd5b8063dbb06c93146105a0578063e4be6e3d146105b2578063e59e4695146105c5578063e6745e13146105d8578063e82f3b70146105eb578063ebf0c717146105fe575f5ffd5b8063bff232c11161011b578063bff232c114610518578063c2b40ae41461052b578063c3a0ec301461054a578063ca2869a01461055b578063cd6dc6871461057a578063da881e5a1461058d575f5ffd5b80639a7a2ffc1461047e5780639f0f874a146104ba578063a0164930146104c3578063a8a4d69b146104e3578063b8ab4704146104f6575f5ffd5b80635d504776116101ed5780638a78bb15116101b25780638a78bb15146104085780638cb89ecb1461041b5780638d1ddfb11461043a5780638da5cb5b146104505780638e5ce3ad146104585780639015d3711461046b575f5ffd5b80635d5047761461038d57806370e36bbe146103a0578063715018a6146103b35780637c92f524146103bb57806385814243146103e8575f5ffd5b80632800d8291161023e5780632800d829146102fc578063291a691b1461030f5780632e7b716d146103325780634d6861a61461034557806350e6d94c146103585780635302670f1461037a575f5ffd5b8063096b810a1461027a578063099a161a1461028f5780630bbfade7146102b55780630f3e3412146102c857806317d61120146102db575b5f5ffd5b61028d61028836600461396b565b6106f9565b005b6102a261029d366004613986565b610845565b6040519081526020015b60405180910390f35b61028d6102c33660046139e1565b61087e565b61028d6102d6366004613986565b610ac2565b6102ee6102e9366004613986565b610b05565b6040516102ac929190613b02565b6102a261030a366004613986565b610cae565b61032261031d366004613b2f565b610cfa565b60405190151581526020016102ac565b61032261034036600461396b565b610ed4565b610322610353366004613986565b610f87565b61032261036636600461396b565b60066020525f908152604090205460ff1681565b61028d61038836600461396b565b610fc6565b61032261039b366004613b68565b611017565b61028d6103ae36600461396b565b61105a565b61028d6110d0565b6103ce6103c9366004613b96565b6110e3565b6040805192835263ffffffff9091166020830152016102ac565b6001546103fb906001600160a01b031681565b6040516102ac9190613bcb565b61028d61041636600461396b565b61128a565b6102a2610429366004613986565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a2565b6103fb6113c8565b600b546103fb906001600160a01b031681565b61032261047936600461396b565b6113f6565b6104a461048c36600461396b565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ac565b6102a260035481565b6104d66104d1366004613986565b611413565b6040516102ac9190613bdf565b6103226104f1366004613b68565b6114a9565b610509610504366004613986565b6114ec565b6040516102ac93929190613bf1565b61028d61052636600461396b565b611637565b6102a2610539366004613986565b60086020525f908152604090205481565b6001546001600160a01b03166103fb565b6102a2610569366004613986565b5f9081526008602052604090205490565b61028d610588366004613c33565b6116af565b61032261059b366004613986565b61180c565b5f546103fb906001600160a01b031681565b600c546103fb906001600160a01b031681565b61028d6105d336600461396b565b611aef565b61028d6105e6366004613c5d565b611b67565b6102a26105f9366004613986565b611d2a565b6102a2611d5b565b61060e601481565b60405160ff90911681526020016102ac565b61028d61062e36600461396b565b611d6d565b60045461064f9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ac565b6106ae61067b366004613986565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ac949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a260025481565b6103fb6106f4366004613c5d565b611da7565b6107016113c8565b6001600160a01b0316336001600160a01b0316148061072a57506001546001600160a01b031633145b61074757604051632864c4e160e01b815260040160405180910390fd5b610750816113f6565b8190610779576040516381e5828960e01b81526004016107709190613bcb565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107a79060049083611e14565b6001600160a01b0382165f908152600660205260408120805460ff1916905560028054916107d483613c91565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a602052604081206004810154610874576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b5f888152600a602052604090206002815460ff1660038111156108a3576108a3613ca6565b146108c157604051634f4b461f60e11b815260040160405180910390fd5b6004810154156108e45760405163632a22bb60e01b815260040160405180910390fd5b8561090257604051636caad1ed60e11b815260040160405180910390fd5b5f6109668260060180548060200260200160405190810160405280929190818152602001828054801561095c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161093e575b50505050506120b6565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa1580156109b8573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109df9190810190613e36565b9050806101c00151156109fc576109fc8b828a858b8b8b8b6121bc565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610a5a575f5ffd5b505af1158015610a6c573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610aad96959493929190613ff8565b60405180910390a25050505050505050505050565b610aca6123b8565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610b3b57610b3b613cba565b604051908082528060200260200182016040528015610b64578160200160208202803683370190505b509450806001600160401b03811115610b7f57610b7f613cba565b604051908082528060200260200182016040528015610ba8578160200160208202803683370190505b5093505f805b83811015610ca4575f856006018281548110610bcc57610bcc614045565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610c1257610c12613ca6565b03610c9b5780888481518110610c2a57610c2a614045565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610c8257610c82614045565b602090810291909101015282610c9781614059565b9350505b50600101610bae565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610cd257610cd2613ca6565b03610cf057604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d255760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d4957610d49613ca6565b14610d67576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610dae573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd29190614071565b905080610de5604086016020870161409b565b63ffffffff161115610dfd604086016020870161409b565b829091610e2b576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610770565b5050815460ff1916600190811783558201859055436002830155600354610e5290426140b4565b6003830155610e6660058301856002613869565b50610e6f611d5b565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ec0928a928a92916140c7565b60405180910390a250600195945050505050565b5f610ede826113f6565b610ee957505f919050565b6001546001600160a01b0316610f12576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f42908590600401613bcb565b602060405180830381865afa158015610f5d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f819190614117565b92915050565b5f818152600a602052604081206001815460ff166003811115610fac57610fac613ca6565b14610fb957505f92915050565b6003015442111592915050565b610fce6123b8565b6001600160a01b038116610ff55760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561105257611052613ca6565b149392505050565b6110626123b8565b6001600160a01b0381166110895760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6110d86123b8565b6110e15f6123ea565b565b600b545f9081906001600160a01b031633146111125760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561113757611137613ca6565b1461115557604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561119557611195613ca6565b146111a557600b01549150611282565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916111d983613c91565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161122a929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6112926113c8565b6001600160a01b0316336001600160a01b031614806112bb57506001546001600160a01b031633145b6112d857604051632864c4e160e01b815260040160405180910390fd5b6112e1816113f6565b6113c55760048054600160281b900464ffffffffff169061130b906001600160a01b03841661245a565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161135c83614059565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db5390606001610839565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611446576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561149c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161147e575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156114e3576114e3613ca6565b14159392505050565b5f818152600960205260409020546060908190819061151e576040516322e679e360e11b815260040160405180910390fd5b5f848152600d60209081526040808320600e8352818420600f8452938290208154835181860281018601909452808452919493909291859183018282801561158357602002820191905f5260205f20905b81548152602001906001019080831161156f575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156115d357602002820191905f5260205f20905b8154815260200190600101908083116115bf575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561162357602002820191905f5260205f20905b81548152602001906001019080831161160f575b505050505090509250925092509193909250565b61163f6123b8565b6001600160a01b0381166116665760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f6116b8612630565b805490915060ff600160401b82041615906001600160401b03165f811580156116de5750825b90505f826001600160401b031660011480156116f95750303b155b905081158015611707575080155b156117255760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561174f57845460ff60401b1916600160401b1785555b6001600160a01b0387166117765760405163d92e233d60e01b815260040160405180910390fd5b61177f33612658565b61178b60046014612669565b61179486610ac2565b61179c6113c8565b6001600160a01b0316876001600160a01b0316146117bd576117bd87611d6d565b831561180357845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561183057611830613ca6565b0361184e57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561186657611866613ca6565b1461188457604051631860f69960e31b815260040160405180910390fd5b806003015442116118a857604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061198d578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b15801561196e575f5ffd5b505af1158015611980573d5f5f3e3d5ffd5b505f979650505050505050565b611996826126e8565b815460ff191660021782556006820154600b83018190555f816001600160401b038111156119c6576119c6613cba565b6040519080825280602002602001820160405280156119ef578160200160208202803683370190505b5090505f5b82811015611a6157846009015f866006018381548110611a1657611a16614045565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611a4e57611a4e614045565b60209081029190910101526001016119f4565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611aa4575f5ffd5b505af1158015611ab6573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ec0929190614130565b611af76123b8565b6001600160a01b038116611b1e5760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611b8b57611b8b613ca6565b03611ba957604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611bc157611bc1613ca6565b14611bdf57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611c0457604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611c365760405163257309f160e11b815260040160405180910390fd5b611c3f33610ed4565b611c5c5760405163149fbcfd60e11b815260040160405180910390fd5b611c6733838561280e565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611ce6908390836129df565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611d56576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611d6860046014612be0565b905090565b611d756123b8565b6001600160a01b038116611d9e575f604051631e4fbdf760e01b81526004016107709190613bcb565b6113c5816123ea565b5f828152600a6020526040812060060180548390808210611de4576040516326c5c55b60e11b815260048101929092526024820152604401610770565b5050808381548110611df857611df8614045565b5f918252602090912001546001600160a01b0316949350505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611e535760405162461bcd60e51b815260040161077090614142565b825464ffffffffff600160281b90910481169082168111611eb15760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610770565b825f5b81866001015f611ec48488612cd9565b64ffffffffff1681526020019081526020015f20819055505f816001611eea919061418c565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611f1f57506120ae565b600185165f03611fe6575f611f3e83611f398860016141a5565b612cd9565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f9f916004016141c2565b602060405180830381865af4158015611fba573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fde9190614071565b93505061209a565b5f611ff683611f396001896141f2565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612057916004016141c2565b602060405180830381865af4158015612072573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120969190614071565b9350505b50647fffffffff600194851c169301611eb4565b505050505050565b80515f90816120c682601461420f565b6001600160401b038111156120dd576120dd613cba565b6040519080825280601f01601f191660200182016040528015612107576020820181803683370190505b5090505f5b828110156121ac575f85828151811061212757612127614045565b602002602001015160601b90505f826014612142919061420f565b90505f5b601481101561219e5782816014811061216157612161614045565b1a60f81b8561217083856140b4565b8151811061218057612180614045565b60200101906001600160f81b03191690815f1a905350600101612146565b50505080600101905061210c565b5080516020909101209392505050565b826121da57604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b81526004016122119493929190614226565b602060405180830381865afa15801561222c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122509190614117565b61226d5760405163051d8aa760e51b815260040160405180910390fd5b8061228b57604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b03166122b457604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b81526004016123039796959493929190614245565b5f60405180830381865afa15801561231d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612344919081019061431f565b5f8e8152600d60209081526040909120845194975092955090935061236c929086019061390a565b505f8b8152600e60209081526040909120835161238b9285019061390a565b505f8b8152600f6020908152604090912082516123aa9284019061390a565b505050505050505050505050565b336123c16113c8565b6001600160a01b0316146110e1573360405163118cdaa760e01b81526004016107709190613bcb565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106124a95760405162461bcd60e51b815260040161077090614142565b825464ffffffffff908116908216106124fc5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610770565b6125078160016141a5565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f61253e8487612cd9565b64ffffffffff16815260208101919091526040015f20556001831615612629575f61256e82611f396001876141f2565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916125cf916004016141c2565b602060405180830381865af41580156125ea573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061260e9190614071565b647fffffffff600195861c169490935091909101905061252e565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f81565b612660612cf6565b6113c581612d1b565b602060ff821611156126b75760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610770565b6126c8600160ff831681901b614403565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612809575f6127038260016140b4565b90505b82811015612800575f84600601838154811061272457612724614045565b5f9182526020822001546006870180546001600160a01b039092169350908490811061275257612752614045565b5f918252602090912001546001600160a01b03908116915082168110156127f6578086600601858154811061278957612789614045565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550818660060184815481106127ca576127ca614045565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612706565b506001016126ef565b505050565b5f821161282e5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612857576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161288d91614403565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156128d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128f89190614071565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561294b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061296f9190614071565b90505f81116129915760405163aeaddff160e01b815260040160405180910390fd5b5f61299c8284614416565b90505f81116129be5760405163149fbcfd60e11b815260040160405180910390fd5b808611156118035760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612a5d57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612bd9565b5f5f90505f876009015f855f81548110612a7957612a79614045565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612b01575f896009015f878481548110612ac357612ac3614045565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612af8578092508193505b50600101612aa2565b50808610612b15575f945050505050612bd9565b5f88600a015f868581548110612b2d57612b2d614045565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612b6a57612b6a613ca6565b021790555086848381548110612b8257612b82614045565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612c335760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610770565b602060ff83161115612c575760405162461bcd60e51b815260040161077090614435565b8254600160281b900464ffffffffff1680612c7660ff85166002614586565b64ffffffffff161015612cc65760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610770565b612cd1848285612d23565b949350505050565b5f81612cec60ff851663ffffffff61459f565b612bd991906141a5565b612cfe612deb565b6110e157604051631afcd79f60e31b815260040160405180910390fd5b611d75612cf6565b5f602060ff83161115612d485760405162461bcd60e51b815260040161077090614435565b8264ffffffffff165f03612d6657612d5f82612e04565b9050612bd9565b5f612d7283600161418c565b60ff166001600160401b03811115612d8c57612d8c613cba565b604051908082528060200260200182016040528015612db5578160200160208202803683370190505b509050612dc48585858461349e565b808360ff1681518110612dd957612dd9614045565b60200260200101519150509392505050565b5f612df4612630565b54600160401b900460ff16919050565b5f8160ff165f03612e1657505f919050565b8160ff16600103612e4857507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff16600203612e7a57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff16600303612eac57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff16600403612ede57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff16600503612f1057507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff16600603612f4257507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff16600703612f7457507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612fa657507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612fd857507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361300a57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361303c57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361306e57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036130a057507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036130d257507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361310457507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361313657507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361316857507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361319a57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036131cc57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036131fe57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361323057507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361326257507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361329457507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036132c657507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036132f857507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361332a57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361335c57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361338e57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036133c057507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036133f257507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361342457507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361345657507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610770565b602060ff831611156134c25760405162461bcd60e51b815260040161077090614435565b5f8364ffffffffff16116135265760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610770565b5f6135326001856141f2565b9050600181165f0361358557846001015f61354d5f84612cd9565b64ffffffffff1681526020019081526020015f2054825f8151811061357457613574614045565b6020026020010181815250506135ad565b61358e5f612e04565b825f815181106135a0576135a0614045565b6020026020010181815250505b5f5b8360ff168160ff1610156120ae57600182165f036136a55773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061360157613601614045565b6020026020010151815260200161361785612e04565b8152506040518263ffffffff1660e01b815260040161363691906141c2565b602060405180830381865af4158015613651573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136759190614071565b8361368183600161418c565b60ff168151811061369457613694614045565b602002602001018181525050613856565b5f6136b182600161418c565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613753575f876001015f6137088560016136f7919061418c565b60018864ffffffffff16901c612cd9565b64ffffffffff1681526020019081526020015f20549050808584600161372e919061418c565b60ff168151811061374157613741614045565b60200260200101818152505050613854565b5f876001015f61376a85600188611f3991906141f2565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106137c1576137c1614045565b60200260200101518152506040518263ffffffff1660e01b81526004016137e891906141c2565b602060405180830381865af4158015613803573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138279190614071565b8561383385600161418c565b60ff168151811061384657613846614045565b602002602001018181525050505b505b647fffffffff600192831c1691016135af565b6001830191839082156138fa579160200282015f5b838211156138c857833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030261387e565b80156138f85782816101000a81549063ffffffff02191690556004016020816003010492830192600103026138c8565b505b50613906929150613943565b5090565b828054828255905f5260205f209081019282156138fa579160200282015b828111156138fa578251825591602001919060010190613928565b5b80821115613906575f8155600101613944565b6001600160a01b03811681146113c5575f5ffd5b5f6020828403121561397b575f5ffd5b8135612bd981613957565b5f60208284031215613996575f5ffd5b5035919050565b5f5f83601f8401126139ad575f5ffd5b5081356001600160401b038111156139c3575f5ffd5b6020830191508360208285010111156139da575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b0312156139f8575f5ffd5b8835975060208901356001600160401b03811115613a14575f5ffd5b613a208b828c0161399d565b9098509650506040890135945060608901356001600160401b03811115613a45575f5ffd5b613a518b828c0161399d565b90955093505060808901356001600160401b03811115613a6f575f5ffd5b613a7b8b828c0161399d565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613ac85781516001600160a01b0316865260209586019590910190600101613aa1565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613ac8578151865260209586019590910190600101613ae4565b604081525f613b146040830185613a8f565b8281036020840152613b268185613ad2565b95945050505050565b5f5f5f60808486031215613b41575f5ffd5b833592506020840135915060808401851015613b5b575f5ffd5b6040840190509250925092565b5f5f60408385031215613b79575f5ffd5b823591506020830135613b8b81613957565b809150509250929050565b5f5f5f60608486031215613ba8575f5ffd5b833592506020840135613bba81613957565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612bd96020830184613a8f565b606081525f613c036060830186613ad2565b8281036020840152613c158186613ad2565b90508281036040840152613c298185613ad2565b9695505050505050565b5f5f60408385031215613c44575f5ffd5b8235613c4f81613957565b946020939093013593505050565b5f5f60408385031215613c6e575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613c9f57613c9f613c7d565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613cf157613cf1613cba565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613d1f57613d1f613cba565b604052919050565b805160048110611d56575f5ffd5b5f82601f830112613d44575f5ffd5b604080519081016001600160401b0381118282101715613d6657613d66613cba565b8060405250806040840185811115613d7c575f5ffd5b845b81811015613d96578051835260209283019201613d7e565b509195945050505050565b8051611d5681613957565b805160ff81168114611d56575f5ffd5b5f82601f830112613dcb575f5ffd5b81516001600160401b03811115613de457613de4613cba565b613df7601f8201601f1916602001613cf7565b818152846020838601011115613e0b575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611d56575f5ffd5b5f60208284031215613e46575f5ffd5b81516001600160401b03811115613e5b575f5ffd5b82016102008185031215613e6d575f5ffd5b613e75613cce565b81518152613e8560208301613d27565b602082015260408281015190820152613ea18560608401613d35565b606082015260a08201516080820152613ebc60c08301613da1565b60a0820152613ecd60e08301613dac565b60c08201526101008201516001600160401b03811115613eeb575f5ffd5b613ef786828501613dbc565b60e083015250613f0a6101208301613da1565b610100820152613f1d6101408301613da1565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613f53575f5ffd5b613f5f86828501613dbc565b61018083015250613f736101c08301613da1565b6101a0820152613f866101e08301613e27565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613ac85781546001600160a01b0316865260209095019460019182019101613fa9565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f61400a6080830189613f94565b828103602084015261401d81888a613fd0565b90508560408401528281036060840152614038818587613fd0565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161406a5761406a613c7d565b5060010190565b5f60208284031215614081575f5ffd5b5051919050565b803563ffffffff81168114611d56575f5ffd5b5f602082840312156140ab575f5ffd5b612bd982614088565b80820180821115610f8157610f81613c7d565b84815260a0810160208201855f5b60028110156141025763ffffffff6140ec83614088565b16835260209283019291909101906001016140d5565b50505060608201939093526080015292915050565b5f60208284031215614127575f5ffd5b612bd982613e27565b604081525f613b146040830185613f94565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f8157610f81613c7d565b64ffffffffff8181168382160190811115610f8157610f81613c7d565b6040810181835f5b60028110156141e95781518352602092830192909101906001016141ca565b50505092915050565b64ffffffffff8281168282160390811115610f8157610f81613c7d565b8082028115828204841417610f8157610f81613c7d565b848152836020820152606060408201525f613c29606083018486613fd0565b60018060a01b038816815286602082015285604082015260a060608201525f61427260a083018688613fd0565b8281036080840152614285818587613fd0565b9a9950505050505050505050565b5f6001600160401b038211156142ab576142ab613cba565b5060051b60200190565b5f82601f8301126142c4575f5ffd5b81516142d76142d282614293565b613cf7565b8082825260208201915060208360051b8601019250858311156142f8575f5ffd5b602085015b838110156143155780518352602092830192016142fd565b5095945050505050565b5f5f5f60608486031215614331575f5ffd5b83516001600160401b03811115614346575f5ffd5b8401601f81018613614356575f5ffd5b80516143646142d282614293565b8082825260208201915060208360051b850101925088831115614385575f5ffd5b6020840193505b828410156143a757835182526020938401939091019061438c565b8096505050505060208401516001600160401b038111156143c6575f5ffd5b6143d2868287016142b5565b92505060408401516001600160401b038111156143ed575f5ffd5b6143f9868287016142b5565b9150509250925092565b81810381811115610f8157610f81613c7d565b5f8261443057634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156112825780850481111561449757614497613c7d565b60018416156144a557908102905b60019390931c92800261447c565b5f826144c157506001610f81565b816144cd57505f610f81565b81600181146144e357600281146144ed5761451f565b6001915050610f81565b60ff8411156144fe576144fe613c7d565b6001841b915064ffffffffff82111561451957614519613c7d565b50610f81565b5060208310610133831016604e8410600b8410161715614557575081810a64ffffffffff81111561455257614552613c7d565b610f81565b61456764ffffffffff8484614478565b8064ffffffffff0482111561457e5761457e613c7d565b029392505050565b5f612bd964ffffffffff841664ffffffffff84166144b3565b64ffffffffff81811683821602908116908181146145bf576145bf613c7d565b509291505056fea164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b5060043610610276575f3560e01c80639a7a2ffc11610156578063dbb06c93116100ca578063f165053611610084578063f165053614610606578063f2fde38b14610620578063f379b0df14610633578063f52fd8031461066d578063f6fc05d5146106dd578063fa932569146106e6575f5ffd5b8063dbb06c93146105a0578063e4be6e3d146105b2578063e59e4695146105c5578063e6745e13146105d8578063e82f3b70146105eb578063ebf0c717146105fe575f5ffd5b8063bff232c11161011b578063bff232c114610518578063c2b40ae41461052b578063c3a0ec301461054a578063ca2869a01461055b578063cd6dc6871461057a578063da881e5a1461058d575f5ffd5b80639a7a2ffc1461047e5780639f0f874a146104ba578063a0164930146104c3578063a8a4d69b146104e3578063b8ab4704146104f6575f5ffd5b80635d504776116101ed5780638a78bb15116101b25780638a78bb15146104085780638cb89ecb1461041b5780638d1ddfb11461043a5780638da5cb5b146104505780638e5ce3ad146104585780639015d3711461046b575f5ffd5b80635d5047761461038d57806370e36bbe146103a0578063715018a6146103b35780637c92f524146103bb57806385814243146103e8575f5ffd5b80632800d8291161023e5780632800d829146102fc578063291a691b1461030f5780632e7b716d146103325780634d6861a61461034557806350e6d94c146103585780635302670f1461037a575f5ffd5b8063096b810a1461027a578063099a161a1461028f5780630bbfade7146102b55780630f3e3412146102c857806317d61120146102db575b5f5ffd5b61028d61028836600461396b565b6106f9565b005b6102a261029d366004613986565b610845565b6040519081526020015b60405180910390f35b61028d6102c33660046139e1565b61087e565b61028d6102d6366004613986565b610ac2565b6102ee6102e9366004613986565b610b05565b6040516102ac929190613b02565b6102a261030a366004613986565b610cae565b61032261031d366004613b2f565b610cfa565b60405190151581526020016102ac565b61032261034036600461396b565b610ed4565b610322610353366004613986565b610f87565b61032261036636600461396b565b60066020525f908152604090205460ff1681565b61028d61038836600461396b565b610fc6565b61032261039b366004613b68565b611017565b61028d6103ae36600461396b565b61105a565b61028d6110d0565b6103ce6103c9366004613b96565b6110e3565b6040805192835263ffffffff9091166020830152016102ac565b6001546103fb906001600160a01b031681565b6040516102ac9190613bcb565b61028d61041636600461396b565b61128a565b6102a2610429366004613986565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a2565b6103fb6113c8565b600b546103fb906001600160a01b031681565b61032261047936600461396b565b6113f6565b6104a461048c36600461396b565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ac565b6102a260035481565b6104d66104d1366004613986565b611413565b6040516102ac9190613bdf565b6103226104f1366004613b68565b6114a9565b610509610504366004613986565b6114ec565b6040516102ac93929190613bf1565b61028d61052636600461396b565b611637565b6102a2610539366004613986565b60086020525f908152604090205481565b6001546001600160a01b03166103fb565b6102a2610569366004613986565b5f9081526008602052604090205490565b61028d610588366004613c33565b6116af565b61032261059b366004613986565b61180c565b5f546103fb906001600160a01b031681565b600c546103fb906001600160a01b031681565b61028d6105d336600461396b565b611aef565b61028d6105e6366004613c5d565b611b67565b6102a26105f9366004613986565b611d2a565b6102a2611d5b565b61060e601481565b60405160ff90911681526020016102ac565b61028d61062e36600461396b565b611d6d565b60045461064f9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ac565b6106ae61067b366004613986565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ac949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a260025481565b6103fb6106f4366004613c5d565b611da7565b6107016113c8565b6001600160a01b0316336001600160a01b0316148061072a57506001546001600160a01b031633145b61074757604051632864c4e160e01b815260040160405180910390fd5b610750816113f6565b8190610779576040516381e5828960e01b81526004016107709190613bcb565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107a79060049083611e14565b6001600160a01b0382165f908152600660205260408120805460ff1916905560028054916107d483613c91565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a602052604081206004810154610874576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b5f888152600a602052604090206002815460ff1660038111156108a3576108a3613ca6565b146108c157604051634f4b461f60e11b815260040160405180910390fd5b6004810154156108e45760405163632a22bb60e01b815260040160405180910390fd5b8561090257604051636caad1ed60e11b815260040160405180910390fd5b5f6109668260060180548060200260200160405190810160405280929190818152602001828054801561095c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161093e575b50505050506120b6565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa1580156109b8573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526109df9190810190613e36565b9050806101c00151156109fc576109fc8b828a858b8b8b8b6121bc565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610a5a575f5ffd5b505af1158015610a6c573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610aad96959493929190613ff8565b60405180910390a25050505050505050505050565b610aca6123b8565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610b3b57610b3b613cba565b604051908082528060200260200182016040528015610b64578160200160208202803683370190505b509450806001600160401b03811115610b7f57610b7f613cba565b604051908082528060200260200182016040528015610ba8578160200160208202803683370190505b5093505f805b83811015610ca4575f856006018281548110610bcc57610bcc614045565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610c1257610c12613ca6565b03610c9b5780888481518110610c2a57610c2a614045565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610c8257610c82614045565b602090810291909101015282610c9781614059565b9350505b50600101610bae565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610cd257610cd2613ca6565b03610cf057604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610d255760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610d4957610d49613ca6565b14610d67576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610dae573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd29190614071565b905080610de5604086016020870161409b565b63ffffffff161115610dfd604086016020870161409b565b829091610e2b576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610770565b5050815460ff1916600190811783558201859055436002830155600354610e5290426140b4565b6003830155610e6660058301856002613869565b50610e6f611d5b565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ec0928a928a92916140c7565b60405180910390a250600195945050505050565b5f610ede826113f6565b610ee957505f919050565b6001546001600160a01b0316610f12576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610f42908590600401613bcb565b602060405180830381865afa158015610f5d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f819190614117565b92915050565b5f818152600a602052604081206001815460ff166003811115610fac57610fac613ca6565b14610fb957505f92915050565b6003015442111592915050565b610fce6123b8565b6001600160a01b038116610ff55760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0392909216919091179055565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561105257611052613ca6565b149392505050565b6110626123b8565b6001600160a01b0381166110895760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6110d86123b8565b6110e15f6123ea565b565b600b545f9081906001600160a01b031633146111125760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561113757611137613ca6565b1461115557604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561119557611195613ca6565b146111a557600b01549150611282565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916111d983613c91565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161122a929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6112926113c8565b6001600160a01b0316336001600160a01b031614806112bb57506001546001600160a01b031633145b6112d857604051632864c4e160e01b815260040160405180910390fd5b6112e1816113f6565b6113c55760048054600160281b900464ffffffffff169061130b906001600160a01b03841661245a565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161135c83614059565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db5390606001610839565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611446576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561149c57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161147e575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156114e3576114e3613ca6565b14159392505050565b5f818152600960205260409020546060908190819061151e576040516322e679e360e11b815260040160405180910390fd5b5f848152600d60209081526040808320600e8352818420600f8452938290208154835181860281018601909452808452919493909291859183018282801561158357602002820191905f5260205f20905b81548152602001906001019080831161156f575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156115d357602002820191905f5260205f20905b8154815260200190600101908083116115bf575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561162357602002820191905f5260205f20905b81548152602001906001019080831161160f575b505050505090509250925092509193909250565b61163f6123b8565b6001600160a01b0381166116665760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b5f6116b8612630565b805490915060ff600160401b82041615906001600160401b03165f811580156116de5750825b90505f826001600160401b031660011480156116f95750303b155b905081158015611707575080155b156117255760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561174f57845460ff60401b1916600160401b1785555b6001600160a01b0387166117765760405163d92e233d60e01b815260040160405180910390fd5b61177f33612658565b61178b60046014612669565b61179486610ac2565b61179c6113c8565b6001600160a01b0316876001600160a01b0316146117bd576117bd87611d6d565b831561180357845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561183057611830613ca6565b0361184e57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561186657611866613ca6565b1461188457604051631860f69960e31b815260040160405180910390fd5b806003015442116118a857604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061198d578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b15801561196e575f5ffd5b505af1158015611980573d5f5f3e3d5ffd5b505f979650505050505050565b611996826126e8565b815460ff191660021782556006820154600b83018190555f816001600160401b038111156119c6576119c6613cba565b6040519080825280602002602001820160405280156119ef578160200160208202803683370190505b5090505f5b82811015611a6157846009015f866006018381548110611a1657611a16614045565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611a4e57611a4e614045565b60209081029190910101526001016119f4565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611aa4575f5ffd5b505af1158015611ab6573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ec0929190614130565b611af76123b8565b6001600160a01b038116611b1e5760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611b8b57611b8b613ca6565b03611ba957604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611bc157611bc1613ca6565b14611bdf57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611c0457604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611c365760405163257309f160e11b815260040160405180910390fd5b611c3f33610ed4565b611c5c5760405163149fbcfd60e11b815260040160405180910390fd5b611c6733838561280e565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611ce6908390836129df565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611d56576040516322e679e360e11b815260040160405180910390fd5b919050565b5f611d6860046014612be0565b905090565b611d756123b8565b6001600160a01b038116611d9e575f604051631e4fbdf760e01b81526004016107709190613bcb565b6113c5816123ea565b5f828152600a6020526040812060060180548390808210611de4576040516326c5c55b60e11b815260048101929092526024820152604401610770565b5050808381548110611df857611df8614045565b5f918252602090912001546001600160a01b0316949350505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611e535760405162461bcd60e51b815260040161077090614142565b825464ffffffffff600160281b90910481169082168111611eb15760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610770565b825f5b81866001015f611ec48488612cd9565b64ffffffffff1681526020019081526020015f20819055505f816001611eea919061418c565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611f1f57506120ae565b600185165f03611fe6575f611f3e83611f398860016141a5565b612cd9565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f9f916004016141c2565b602060405180830381865af4158015611fba573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fde9190614071565b93505061209a565b5f611ff683611f396001896141f2565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612057916004016141c2565b602060405180830381865af4158015612072573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120969190614071565b9350505b50647fffffffff600194851c169301611eb4565b505050505050565b80515f90816120c682601461420f565b6001600160401b038111156120dd576120dd613cba565b6040519080825280601f01601f191660200182016040528015612107576020820181803683370190505b5090505f5b828110156121ac575f85828151811061212757612127614045565b602002602001015160601b90505f826014612142919061420f565b90505f5b601481101561219e5782816014811061216157612161614045565b1a60f81b8561217083856140b4565b8151811061218057612180614045565b60200101906001600160f81b03191690815f1a905350600101612146565b50505080600101905061210c565b5080516020909101209392505050565b826121da57604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b81526004016122119493929190614226565b602060405180830381865afa15801561222c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122509190614117565b61226d5760405163051d8aa760e51b815260040160405180910390fd5b8061228b57604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b03166122b457604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b81526004016123039796959493929190614245565b5f60405180830381865afa15801561231d573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612344919081019061431f565b5f8e8152600d60209081526040909120845194975092955090935061236c929086019061390a565b505f8b8152600e60209081526040909120835161238b9285019061390a565b505f8b8152600f6020908152604090912082516123aa9284019061390a565b505050505050505050505050565b336123c16113c8565b6001600160a01b0316146110e1573360405163118cdaa760e01b81526004016107709190613bcb565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106124a95760405162461bcd60e51b815260040161077090614142565b825464ffffffffff908116908216106124fc5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610770565b6125078160016141a5565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f61253e8487612cd9565b64ffffffffff16815260208101919091526040015f20556001831615612629575f61256e82611f396001876141f2565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916125cf916004016141c2565b602060405180830381865af41580156125ea573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061260e9190614071565b647fffffffff600195861c169490935091909101905061252e565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610f81565b612660612cf6565b6113c581612d1b565b602060ff821611156126b75760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610770565b6126c8600160ff831681901b614403565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612809575f6127038260016140b4565b90505b82811015612800575f84600601838154811061272457612724614045565b5f9182526020822001546006870180546001600160a01b039092169350908490811061275257612752614045565b5f918252602090912001546001600160a01b03908116915082168110156127f6578086600601858154811061278957612789614045565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550818660060184815481106127ca576127ca614045565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612706565b506001016126ef565b505050565b5f821161282e5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612857576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161288d91614403565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156128d4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128f89190614071565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa15801561294b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061296f9190614071565b90505f81116129915760405163aeaddff160e01b815260040160405180910390fd5b5f61299c8284614416565b90505f81116129be5760405163149fbcfd60e11b815260040160405180910390fd5b808611156118035760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612a5d57508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612bd9565b5f5f90505f876009015f855f81548110612a7957612a79614045565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612b01575f896009015f878481548110612ac357612ac3614045565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612af8578092508193505b50600101612aa2565b50808610612b15575f945050505050612bd9565b5f88600a015f868581548110612b2d57612b2d614045565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612b6a57612b6a613ca6565b021790555086848381548110612b8257612b82614045565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612c335760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610770565b602060ff83161115612c575760405162461bcd60e51b815260040161077090614435565b8254600160281b900464ffffffffff1680612c7660ff85166002614586565b64ffffffffff161015612cc65760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610770565b612cd1848285612d23565b949350505050565b5f81612cec60ff851663ffffffff61459f565b612bd991906141a5565b612cfe612deb565b6110e157604051631afcd79f60e31b815260040160405180910390fd5b611d75612cf6565b5f602060ff83161115612d485760405162461bcd60e51b815260040161077090614435565b8264ffffffffff165f03612d6657612d5f82612e04565b9050612bd9565b5f612d7283600161418c565b60ff166001600160401b03811115612d8c57612d8c613cba565b604051908082528060200260200182016040528015612db5578160200160208202803683370190505b509050612dc48585858461349e565b808360ff1681518110612dd957612dd9614045565b60200260200101519150509392505050565b5f612df4612630565b54600160401b900460ff16919050565b5f8160ff165f03612e1657505f919050565b8160ff16600103612e4857507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff16600203612e7a57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff16600303612eac57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff16600403612ede57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff16600503612f1057507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff16600603612f4257507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff16600703612f7457507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612fa657507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612fd857507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361300a57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361303c57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361306e57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036130a057507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036130d257507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361310457507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361313657507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361316857507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361319a57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036131cc57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036131fe57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361323057507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361326257507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361329457507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036132c657507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036132f857507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361332a57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361335c57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361338e57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036133c057507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036133f257507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361342457507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361345657507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610770565b602060ff831611156134c25760405162461bcd60e51b815260040161077090614435565b5f8364ffffffffff16116135265760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610770565b5f6135326001856141f2565b9050600181165f0361358557846001015f61354d5f84612cd9565b64ffffffffff1681526020019081526020015f2054825f8151811061357457613574614045565b6020026020010181815250506135ad565b61358e5f612e04565b825f815181106135a0576135a0614045565b6020026020010181815250505b5f5b8360ff168160ff1610156120ae57600182165f036136a55773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061360157613601614045565b6020026020010151815260200161361785612e04565b8152506040518263ffffffff1660e01b815260040161363691906141c2565b602060405180830381865af4158015613651573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136759190614071565b8361368183600161418c565b60ff168151811061369457613694614045565b602002602001018181525050613856565b5f6136b182600161418c565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613753575f876001015f6137088560016136f7919061418c565b60018864ffffffffff16901c612cd9565b64ffffffffff1681526020019081526020015f20549050808584600161372e919061418c565b60ff168151811061374157613741614045565b60200260200101818152505050613854565b5f876001015f61376a85600188611f3991906141f2565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106137c1576137c1614045565b60200260200101518152506040518263ffffffff1660e01b81526004016137e891906141c2565b602060405180830381865af4158015613803573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138279190614071565b8561383385600161418c565b60ff168151811061384657613846614045565b602002602001018181525050505b505b647fffffffff600192831c1691016135af565b6001830191839082156138fa579160200282015f5b838211156138c857833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff160217905550926020019260040160208160030104928301926001030261387e565b80156138f85782816101000a81549063ffffffff02191690556004016020816003010492830192600103026138c8565b505b50613906929150613943565b5090565b828054828255905f5260205f209081019282156138fa579160200282015b828111156138fa578251825591602001919060010190613928565b5b80821115613906575f8155600101613944565b6001600160a01b03811681146113c5575f5ffd5b5f6020828403121561397b575f5ffd5b8135612bd981613957565b5f60208284031215613996575f5ffd5b5035919050565b5f5f83601f8401126139ad575f5ffd5b5081356001600160401b038111156139c3575f5ffd5b6020830191508360208285010111156139da575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b0312156139f8575f5ffd5b8835975060208901356001600160401b03811115613a14575f5ffd5b613a208b828c0161399d565b9098509650506040890135945060608901356001600160401b03811115613a45575f5ffd5b613a518b828c0161399d565b90955093505060808901356001600160401b03811115613a6f575f5ffd5b613a7b8b828c0161399d565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613ac85781516001600160a01b0316865260209586019590910190600101613aa1565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613ac8578151865260209586019590910190600101613ae4565b604081525f613b146040830185613a8f565b8281036020840152613b268185613ad2565b95945050505050565b5f5f5f60808486031215613b41575f5ffd5b833592506020840135915060808401851015613b5b575f5ffd5b6040840190509250925092565b5f5f60408385031215613b79575f5ffd5b823591506020830135613b8b81613957565b809150509250929050565b5f5f5f60608486031215613ba8575f5ffd5b833592506020840135613bba81613957565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f612bd96020830184613a8f565b606081525f613c036060830186613ad2565b8281036020840152613c158186613ad2565b90508281036040840152613c298185613ad2565b9695505050505050565b5f5f60408385031215613c44575f5ffd5b8235613c4f81613957565b946020939093013593505050565b5f5f60408385031215613c6e575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613c9f57613c9f613c7d565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613cf157613cf1613cba565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613d1f57613d1f613cba565b604052919050565b805160048110611d56575f5ffd5b5f82601f830112613d44575f5ffd5b604080519081016001600160401b0381118282101715613d6657613d66613cba565b8060405250806040840185811115613d7c575f5ffd5b845b81811015613d96578051835260209283019201613d7e565b509195945050505050565b8051611d5681613957565b805160ff81168114611d56575f5ffd5b5f82601f830112613dcb575f5ffd5b81516001600160401b03811115613de457613de4613cba565b613df7601f8201601f1916602001613cf7565b818152846020838601011115613e0b575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114611d56575f5ffd5b5f60208284031215613e46575f5ffd5b81516001600160401b03811115613e5b575f5ffd5b82016102008185031215613e6d575f5ffd5b613e75613cce565b81518152613e8560208301613d27565b602082015260408281015190820152613ea18560608401613d35565b606082015260a08201516080820152613ebc60c08301613da1565b60a0820152613ecd60e08301613dac565b60c08201526101008201516001600160401b03811115613eeb575f5ffd5b613ef786828501613dbc565b60e083015250613f0a6101208301613da1565b610100820152613f1d6101408301613da1565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613f53575f5ffd5b613f5f86828501613dbc565b61018083015250613f736101c08301613da1565b6101a0820152613f866101e08301613e27565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613ac85781546001600160a01b0316865260209095019460019182019101613fa9565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f61400a6080830189613f94565b828103602084015261401d81888a613fd0565b90508560408401528281036060840152614038818587613fd0565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161406a5761406a613c7d565b5060010190565b5f60208284031215614081575f5ffd5b5051919050565b803563ffffffff81168114611d56575f5ffd5b5f602082840312156140ab575f5ffd5b612bd982614088565b80820180821115610f8157610f81613c7d565b84815260a0810160208201855f5b60028110156141025763ffffffff6140ec83614088565b16835260209283019291909101906001016140d5565b50505060608201939093526080015292915050565b5f60208284031215614127575f5ffd5b612bd982613e27565b604081525f613b146040830185613f94565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610f8157610f81613c7d565b64ffffffffff8181168382160190811115610f8157610f81613c7d565b6040810181835f5b60028110156141e95781518352602092830192909101906001016141ca565b50505092915050565b64ffffffffff8281168282160390811115610f8157610f81613c7d565b8082028115828204841417610f8157610f81613c7d565b848152836020820152606060408201525f613c29606083018486613fd0565b60018060a01b038816815286602082015285604082015260a060608201525f61427260a083018688613fd0565b8281036080840152614285818587613fd0565b9a9950505050505050505050565b5f6001600160401b038211156142ab576142ab613cba565b5060051b60200190565b5f82601f8301126142c4575f5ffd5b81516142d76142d282614293565b613cf7565b8082825260208201915060208360051b8601019250858311156142f8575f5ffd5b602085015b838110156143155780518352602092830192016142fd565b5095945050505050565b5f5f5f60608486031215614331575f5ffd5b83516001600160401b03811115614346575f5ffd5b8401601f81018613614356575f5ffd5b80516143646142d282614293565b8082825260208201915060208360051b850101925088831115614385575f5ffd5b6020840193505b828410156143a757835182526020938401939091019061438c565b8096505050505060208401516001600160401b038111156143c6575f5ffd5b6143d2868287016142b5565b92505060408401516001600160401b038111156143ed575f5ffd5b6143f9868287016142b5565b9150509250925092565b81810381811115610f8157610f81613c7d565b5f8261443057634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156112825780850481111561449757614497613c7d565b60018416156144a557908102905b60019390931c92800261447c565b5f826144c157506001610f81565b816144cd57505f610f81565b81600181146144e357600281146144ed5761451f565b6001915050610f81565b60ff8411156144fe576144fe613c7d565b6001841b915064ffffffffff82111561451957614519613c7d565b50610f81565b5060208310610133831016604e8410600b8410161715614557575081810a64ffffffffff81111561455257614552613c7d565b610f81565b61456764ffffffffff8484614478565b8064ffffffffff0482111561457e5761457e613c7d565b029392505050565b5f612bd964ffffffffff841664ffffffffff84166144b3565b64ffffffffff81811683821602908116908181146145bf576145bf613c7d565b509291505056fea164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6148dd806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102b8575f3560e01c80639a7a2ffc11610177578063da881e5a116100d5578063ebf0c7171161008f578063ebf0c71714610694578063f16505361461069c578063f2fde38b146106b6578063f379b0df146106c9578063f52fd80314610703578063f6fc05d514610773578063fa9325691461077c575f5ffd5b8063da881e5a14610623578063dbb06c9314610636578063e4be6e3d14610648578063e59e46951461065b578063e6745e131461066e578063e82f3b7014610681575f5ffd5b8063b8ab470411610131578063b8ab470414610584578063bff232c1146105a6578063c2b40ae4146105b9578063c3a0ec30146105d8578063c8fe182d146105e9578063ca2869a0146105f1578063cd6dc68714610610575f5ffd5b80639a7a2ffc146104f05780639f0f874a1461052c578063a016493014610535578063a8a4d69b14610555578063acc5249414610568578063b2d5d1ac1461057b575f5ffd5b806350e6fad31161022457806385814243116101de57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ac5780638da5cb5b146104c25780638e5ce3ad146104ca5780639015d371146104dd575f5ffd5b806350e6fad3146103e25780635d504776146103ec57806362cc89a8146103ff57806370e36bbe1461041f578063715018a6146104325780637c92f5241461043a575f5ffd5b80632800d829116102755780632800d82914610351578063291a691b146103645780632e7b716d14610387578063323beaa51461039a5780634d6861a6146103ad57806350e6d94c146103c0575f5ffd5b8063096b810a146102bc578063099a161a146102d15780630b45f8e9146102f75780630bbfade71461030a5780630f3e34121461031d57806317d6112014610330575b5f5ffd5b6102cf6102ca366004613c75565b61078f565b005b6102e46102df366004613c90565b6108db565b6040519081526020015b60405180910390f35b6102cf610305366004613c75565b610914565b6102cf610318366004613ceb565b6109b5565b6102cf61032b366004613c90565b610bf9565b61034361033e366004613c90565b610c3c565b6040516102ee929190613e0c565b6102e461035f366004613c90565b610de5565b610377610372366004613e39565b610e31565b60405190151581526020016102ee565b610377610395366004613c75565b61100b565b6102cf6103a8366004613c75565b6110be565b6103776103bb366004613c90565b6111cf565b6103776103ce366004613c75565b60066020525f908152604090205460ff1681565b6102e46202a30081565b6103776103fa366004613e72565b61120e565b600d54610412906001600160a01b031681565b6040516102ee9190613ea0565b6102cf61042d366004613c75565b611251565b6102cf6112c7565b61044d610448366004613eb4565b6112da565b6040805192835263ffffffff9091166020830152016102ee565b600154610412906001600160a01b031681565b6102cf610488366004613c75565b611481565b6102e461049b366004613c90565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102e4565b6104126115bf565b600b54610412906001600160a01b031681565b6103776104eb366004613c75565b6115ed565b6105166104fe366004613c75565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ee565b6102e460035481565b610548610543366004613c90565b61160a565b6040516102ee9190613ee9565b610377610563366004613e72565b6116a0565b6102cf610576366004613c75565b6116e3565b6102e4600e5481565b610597610592366004613c90565b61177a565b6040516102ee93929190613efb565b6102cf6105b4366004613c75565b6118c5565b6102e46105c7366004613c90565b60086020525f908152604090205481565b6001546001600160a01b0316610412565b6102cf61193d565b6102e46105ff366004613c90565b5f9081526008602052604090205490565b6102cf61061e366004613f3d565b6119b9565b610377610631366004613c90565b611b16565b5f54610412906001600160a01b031681565b600c54610412906001600160a01b031681565b6102cf610669366004613c75565b611df9565b6102cf61067c366004613f67565b611e71565b6102e461068f366004613c90565b612034565b6102e4612065565b6106a4601481565b60405160ff90911681526020016102ee565b6102cf6106c4366004613c75565b612077565b6004546106e59064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ee565b610744610711366004613c90565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ee949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e460025481565b61041261078a366004613f67565b6120b1565b6107976115bf565b6001600160a01b0316336001600160a01b031614806107c057506001546001600160a01b031633145b6107dd57604051632864c4e160e01b815260040160405180910390fd5b6107e6816115ed565b819061080f576040516381e5828960e01b81526004016108069190613ea0565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff169061083d906004908361211e565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161086a83613f9b565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a60205260408120600481015461090a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61091c6123c0565b600c546001600160a01b0316156109455760405162035f5560e61b815260040160405180910390fd5b6001600160a01b03811661096c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383169081179091556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b905f90a250565b5f888152600a602052604090206002815460ff1660038111156109da576109da613fb0565b146109f857604051634f4b461f60e11b815260040160405180910390fd5b600481015415610a1b5760405163632a22bb60e01b815260040160405180910390fd5b85610a3957604051636caad1ed60e11b815260040160405180910390fd5b5f610a9d82600601805480602002602001604051908101604052809291908181526020018280548015610a9357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610a75575b50505050506123f2565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610aef573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b169190810190614140565b9050806101c0015115610b3357610b338b828a858b8b8b8b6124f8565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610b91575f5ffd5b505af1158015610ba3573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610be496959493929190614302565b60405180910390a25050505050505050505050565b610c016123c0565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610c7257610c72613fc4565b604051908082528060200260200182016040528015610c9b578160200160208202803683370190505b509450806001600160401b03811115610cb657610cb6613fc4565b604051908082528060200260200182016040528015610cdf578160200160208202803683370190505b5093505f805b83811015610ddb575f856006018281548110610d0357610d0361434f565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610d4957610d49613fb0565b03610dd25780888481518110610d6157610d6161434f565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610db957610db961434f565b602090810291909101015282610dce81614363565b9350505b50600101610ce5565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610e0957610e09613fb0565b03610e2757604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610e5c5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610e8057610e80613fb0565b14610e9e576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610ee5573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f09919061437b565b905080610f1c60408601602087016143a5565b63ffffffff161115610f3460408601602087016143a5565b829091610f62576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610806565b5050815460ff1916600190811783558201859055436002830155600354610f8990426143be565b6003830155610f9d60058301856002613b73565b50610fa6612065565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ff7928a928a92916143d1565b60405180910390a250600195945050505050565b5f611015826115ed565b61102057505f919050565b6001546001600160a01b0316611049576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790611079908590600401613ea0565b602060405180830381865afa158015611094573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b89190614421565b92915050565b6110c66123c0565b600d546001600160a01b0316806110f05760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461113157604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610806565b50505f6202a300600e5461114591906143be565b9050804281811015611173576040516337c8270b60e01b815260048101929092526024820152604401610806565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690555f600e8190556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b9190a2505050565b5f818152600a602052604081206001815460ff1660038111156111f4576111f4613fb0565b1461120157505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561124957611249613fb0565b149392505050565b6112596123c0565b6001600160a01b0381166112805760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6112cf6123c0565b6112d85f6126f4565b565b600b545f9081906001600160a01b031633146113095760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561132e5761132e613fb0565b1461134c57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561138c5761138c613fb0565b1461139c57600b01549150611479565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916113d083613f9b565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611421929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6114896115bf565b6001600160a01b0316336001600160a01b031614806114b257506001546001600160a01b031633145b6114cf57604051632864c4e160e01b815260040160405180910390fd5b6114d8816115ed565b6115bc5760048054600160281b900464ffffffffff1690611502906001600160a01b038416612764565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161155383614363565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016108cf565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a6020526040902060048101546060919061163d576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561169357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611675575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156116da576116da613fb0565b14159392505050565b6116eb6123c0565b6001600160a01b0381166117125760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611766906202a300906143be565b60405190815260200160405180910390a250565b5f81815260096020526040902054606090819081906117ac576040516322e679e360e11b815260040160405180910390fd5b5f848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561181157602002820191905f5260205f20905b8154815260200190600101908083116117fd575b505050505092508180548060200260200160405190810160405280929190818152602001828054801561186157602002820191905f5260205f20905b81548152602001906001019080831161184d575b50505050509150808054806020026020016040519081016040528092919081815260200182805480156118b157602002820191905f5260205f20905b81548152602001906001019080831161189d575b505050505090509250925092509193909250565b6118cd6123c0565b6001600160a01b0381166118f45760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6119456123c0565b600d546001600160a01b03168061196f5760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690555f600e8190556040516001600160a01b038316917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a250565b5f6119c261293a565b805490915060ff600160401b82041615906001600160401b03165f811580156119e85750825b90505f826001600160401b03166001148015611a035750303b155b905081158015611a11575080155b15611a2f5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611a5957845460ff60401b1916600160401b1785555b6001600160a01b038716611a805760405163d92e233d60e01b815260040160405180910390fd5b611a8933612962565b611a9560046014612973565b611a9e86610bf9565b611aa66115bf565b6001600160a01b0316876001600160a01b031614611ac757611ac787612077565b8315611b0d57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff166003811115611b3a57611b3a613fb0565b03611b5857604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611b7057611b70613fb0565b14611b8e57604051631860f69960e31b815260040160405180910390fd5b80600301544211611bb257604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611c97578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611c78575f5ffd5b505af1158015611c8a573d5f5f3e3d5ffd5b505f979650505050505050565b611ca0826129f2565b815460ff191660021782556006820154600b83018190555f816001600160401b03811115611cd057611cd0613fc4565b604051908082528060200260200182016040528015611cf9578160200160208202803683370190505b5090505f5b82811015611d6b57846009015f866006018381548110611d2057611d2061434f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611d5857611d5861434f565b6020908102919091010152600101611cfe565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611dae575f5ffd5b505af1158015611dc0573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ff792919061443a565b611e016123c0565b6001600160a01b038116611e285760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611e9557611e95613fb0565b03611eb357604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611ecb57611ecb613fb0565b14611ee957604051631860f69960e31b815260040160405180910390fd5b8060030154421115611f0e57604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611f405760405163257309f160e11b815260040160405180910390fd5b611f493361100b565b611f665760405163149fbcfd60e11b815260040160405180910390fd5b611f71338385612b18565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611ff090839083612ce9565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480612060576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61207260046014612eea565b905090565b61207f6123c0565b6001600160a01b0381166120a8575f604051631e4fbdf760e01b81526004016108069190613ea0565b6115bc816126f4565b5f828152600a60205260408120600601805483908082106120ee576040516326c5c55b60e11b815260048101929092526024820152604401610806565b50508083815481106121025761210261434f565b5f918252602090912001546001600160a01b0316949350505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061215d5760405162461bcd60e51b81526004016108069061444c565b825464ffffffffff600160281b909104811690821681116121bb5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610806565b825f5b81866001015f6121ce8488612fe3565b64ffffffffff1681526020019081526020015f20819055505f8160016121f49190614496565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff16811161222957506123b8565b600185165f036122f0575f612248836122438860016144af565b612fe3565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916122a9916004016144cc565b602060405180830381865af41580156122c4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e8919061437b565b9350506123a4565b5f612300836122436001896144fc565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612361916004016144cc565b602060405180830381865af415801561237c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123a0919061437b565b9350505b50647fffffffff600194851c1693016121be565b505050505050565b336123c96115bf565b6001600160a01b0316146112d8573360405163118cdaa760e01b81526004016108069190613ea0565b80515f9081612402826014614519565b6001600160401b0381111561241957612419613fc4565b6040519080825280601f01601f191660200182016040528015612443576020820181803683370190505b5090505f5b828110156124e8575f8582815181106124635761246361434f565b602002602001015160601b90505f82601461247e9190614519565b90505f5b60148110156124da5782816014811061249d5761249d61434f565b1a60f81b856124ac83856143be565b815181106124bc576124bc61434f565b60200101906001600160f81b03191690815f1a905350600101612482565b505050806001019050612448565b5080516020909101209392505050565b8261251657604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b815260040161254d9493929190614530565b602060405180830381865afa158015612568573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061258c9190614421565b6125a95760405163051d8aa760e51b815260040160405180910390fd5b806125c757604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b03166125f057604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b815260040161263f979695949392919061454f565b5f60405180830381865afa158015612659573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526126809190810190614629565b5f8e8152600f6020908152604090912084519497509295509093506126a89290860190613c14565b505f8b815260106020908152604090912083516126c792850190613c14565b505f8b815260116020908152604090912082516126e692840190613c14565b505050505050505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106127b35760405162461bcd60e51b81526004016108069061444c565b825464ffffffffff908116908216106128065760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610806565b6128118160016144af565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6128488487612fe3565b64ffffffffff16815260208101919091526040015f20556001831615612933575f612878826122436001876144fc565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916128d9916004016144cc565b602060405180830381865af41580156128f4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612918919061437b565b647fffffffff600195861c1694909350919091019050612838565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006110b8565b61296a613000565b6115bc81613025565b602060ff821611156129c15760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610806565b6129d2600160ff831681901b61470d565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612b13575f612a0d8260016143be565b90505b82811015612b0a575f846006018381548110612a2e57612a2e61434f565b5f9182526020822001546006870180546001600160a01b0390921693509084908110612a5c57612a5c61434f565b5f918252602090912001546001600160a01b0390811691508216811015612b005780866006018581548110612a9357612a9361434f565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612ad457612ad461434f565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612a10565b506001016129f9565b505050565b5f8211612b385760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612b61576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612b979161470d565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612bde573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c02919061437b565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c55573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c79919061437b565b90505f8111612c9b5760405163aeaddff160e01b815260040160405180910390fd5b5f612ca68284614720565b90505f8111612cc85760405163149fbcfd60e11b815260040160405180910390fd5b80861115611b0d5760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612d6757508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612ee3565b5f5f90505f876009015f855f81548110612d8357612d8361434f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612e0b575f896009015f878481548110612dcd57612dcd61434f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e02578092508193505b50600101612dac565b50808610612e1f575f945050505050612ee3565b5f88600a015f868581548110612e3757612e3761434f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612e7457612e74613fb0565b021790555086848381548110612e8c57612e8c61434f565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612f3d5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610806565b602060ff83161115612f615760405162461bcd60e51b81526004016108069061473f565b8254600160281b900464ffffffffff1680612f8060ff85166002614890565b64ffffffffff161015612fd05760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610806565b612fdb84828561302d565b949350505050565b5f81612ff660ff851663ffffffff6148a9565b612ee391906144af565b6130086130f5565b6112d857604051631afcd79f60e31b815260040160405180910390fd5b61207f613000565b5f602060ff831611156130525760405162461bcd60e51b81526004016108069061473f565b8264ffffffffff165f03613070576130698261310e565b9050612ee3565b5f61307c836001614496565b60ff166001600160401b0381111561309657613096613fc4565b6040519080825280602002602001820160405280156130bf578160200160208202803683370190505b5090506130ce858585846137a8565b808360ff16815181106130e3576130e361434f565b60200260200101519150509392505050565b5f6130fe61293a565b54600160401b900460ff16919050565b5f8160ff165f0361312057505f919050565b8160ff1660010361315257507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361318457507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036131b657507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036131e857507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361321a57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361324c57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361327e57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036132b057507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036132e257507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361331457507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361334657507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361337857507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036133aa57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036133dc57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361340e57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361344057507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361347257507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff166012036134a457507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036134d657507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361350857507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361353a57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361356c57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361359e57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036135d057507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361360257507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361363457507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361366657507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361369857507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036136ca57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036136fc57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361372e57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361376057507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610806565b602060ff831611156137cc5760405162461bcd60e51b81526004016108069061473f565b5f8364ffffffffff16116138305760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610806565b5f61383c6001856144fc565b9050600181165f0361388f57846001015f6138575f84612fe3565b64ffffffffff1681526020019081526020015f2054825f8151811061387e5761387e61434f565b6020026020010181815250506138b7565b6138985f61310e565b825f815181106138aa576138aa61434f565b6020026020010181815250505b5f5b8360ff168160ff1610156123b857600182165f036139af5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061390b5761390b61434f565b602002602001015181526020016139218561310e565b8152506040518263ffffffff1660e01b815260040161394091906144cc565b602060405180830381865af415801561395b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061397f919061437b565b8361398b836001614496565b60ff168151811061399e5761399e61434f565b602002602001018181525050613b60565b5f6139bb826001614496565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613a5d575f876001015f613a12856001613a019190614496565b60018864ffffffffff16901c612fe3565b64ffffffffff1681526020019081526020015f205490508085846001613a389190614496565b60ff1681518110613a4b57613a4b61434f565b60200260200101818152505050613b5e565b5f876001015f613a748560018861224391906144fc565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613acb57613acb61434f565b60200260200101518152506040518263ffffffff1660e01b8152600401613af291906144cc565b602060405180830381865af4158015613b0d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b31919061437b565b85613b3d856001614496565b60ff1681518110613b5057613b5061434f565b602002602001018181525050505b505b647fffffffff600192831c1691016138b9565b600183019183908215613c04579160200282015f5b83821115613bd257833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613b88565b8015613c025782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613bd2565b505b50613c10929150613c4d565b5090565b828054828255905f5260205f20908101928215613c04579160200282015b82811115613c04578251825591602001919060010190613c32565b5b80821115613c10575f8155600101613c4e565b6001600160a01b03811681146115bc575f5ffd5b5f60208284031215613c85575f5ffd5b8135612ee381613c61565b5f60208284031215613ca0575f5ffd5b5035919050565b5f5f83601f840112613cb7575f5ffd5b5081356001600160401b03811115613ccd575f5ffd5b602083019150836020828501011115613ce4575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613d02575f5ffd5b8835975060208901356001600160401b03811115613d1e575f5ffd5b613d2a8b828c01613ca7565b9098509650506040890135945060608901356001600160401b03811115613d4f575f5ffd5b613d5b8b828c01613ca7565b90955093505060808901356001600160401b03811115613d79575f5ffd5b613d858b828c01613ca7565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613dd25781516001600160a01b0316865260209586019590910190600101613dab565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613dd2578151865260209586019590910190600101613dee565b604081525f613e1e6040830185613d99565b8281036020840152613e308185613ddc565b95945050505050565b5f5f5f60808486031215613e4b575f5ffd5b833592506020840135915060808401851015613e65575f5ffd5b6040840190509250925092565b5f5f60408385031215613e83575f5ffd5b823591506020830135613e9581613c61565b809150509250929050565b6001600160a01b0391909116815260200190565b5f5f5f60608486031215613ec6575f5ffd5b833592506020840135613ed881613c61565b929592945050506040919091013590565b602081525f612ee36020830184613d99565b606081525f613f0d6060830186613ddc565b8281036020840152613f1f8186613ddc565b90508281036040840152613f338185613ddc565b9695505050505050565b5f5f60408385031215613f4e575f5ffd5b8235613f5981613c61565b946020939093013593505050565b5f5f60408385031215613f78575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613fa957613fa9613f87565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613ffb57613ffb613fc4565b60405290565b604051601f8201601f191681016001600160401b038111828210171561402957614029613fc4565b604052919050565b805160048110612060575f5ffd5b5f82601f83011261404e575f5ffd5b604080519081016001600160401b038111828210171561407057614070613fc4565b8060405250806040840185811115614086575f5ffd5b845b818110156140a0578051835260209283019201614088565b509195945050505050565b805161206081613c61565b805160ff81168114612060575f5ffd5b5f82601f8301126140d5575f5ffd5b81516001600160401b038111156140ee576140ee613fc4565b614101601f8201601f1916602001614001565b818152846020838601011115614115575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114612060575f5ffd5b5f60208284031215614150575f5ffd5b81516001600160401b03811115614165575f5ffd5b82016102008185031215614177575f5ffd5b61417f613fd8565b8151815261418f60208301614031565b6020820152604082810151908201526141ab856060840161403f565b606082015260a082015160808201526141c660c083016140ab565b60a08201526141d760e083016140b6565b60c08201526101008201516001600160401b038111156141f5575f5ffd5b614201868285016140c6565b60e08301525061421461012083016140ab565b61010082015261422761014083016140ab565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561425d575f5ffd5b614269868285016140c6565b6101808301525061427d6101c083016140ab565b6101a08201526142906101e08301614131565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613dd25781546001600160a01b03168652602090950194600191820191016142b3565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f614314608083018961429e565b828103602084015261432781888a6142da565b905085604084015282810360608401526143428185876142da565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161437457614374613f87565b5060010190565b5f6020828403121561438b575f5ffd5b5051919050565b803563ffffffff81168114612060575f5ffd5b5f602082840312156143b5575f5ffd5b612ee382614392565b808201808211156110b8576110b8613f87565b84815260a0810160208201855f5b600281101561440c5763ffffffff6143f683614392565b16835260209283019291909101906001016143df565b50505060608201939093526080015292915050565b5f60208284031215614431575f5ffd5b612ee382614131565b604081525f613e1e604083018561429e565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff81811683821601908111156110b8576110b8613f87565b64ffffffffff81811683821601908111156110b8576110b8613f87565b6040810181835f5b60028110156144f35781518352602092830192909101906001016144d4565b50505092915050565b64ffffffffff82811682821603908111156110b8576110b8613f87565b80820281158282048414176110b8576110b8613f87565b848152836020820152606060408201525f613f336060830184866142da565b60018060a01b038816815286602082015285604082015260a060608201525f61457c60a0830186886142da565b828103608084015261458f8185876142da565b9a9950505050505050505050565b5f6001600160401b038211156145b5576145b5613fc4565b5060051b60200190565b5f82601f8301126145ce575f5ffd5b81516145e16145dc8261459d565b614001565b8082825260208201915060208360051b860101925085831115614602575f5ffd5b602085015b8381101561461f578051835260209283019201614607565b5095945050505050565b5f5f5f6060848603121561463b575f5ffd5b83516001600160401b03811115614650575f5ffd5b8401601f81018613614660575f5ffd5b805161466e6145dc8261459d565b8082825260208201915060208360051b85010192508883111561468f575f5ffd5b6020840193505b828410156146b1578351825260209384019390910190614696565b8096505050505060208401516001600160401b038111156146d0575f5ffd5b6146dc868287016145bf565b92505060408401516001600160401b038111156146f7575f5ffd5b614703868287016145bf565b9150509250925092565b818103818111156110b8576110b8613f87565b5f8261473a57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115611479578085048111156147a1576147a1613f87565b60018416156147af57908102905b60019390931c928002614786565b5f826147cb575060016110b8565b816147d757505f6110b8565b81600181146147ed57600281146147f757614829565b60019150506110b8565b60ff84111561480857614808613f87565b6001841b915064ffffffffff82111561482357614823613f87565b506110b8565b5060208310610133831016604e8410600b8410161715614861575081810a64ffffffffff81111561485c5761485c613f87565b6110b8565b61487164ffffffffff8484614782565b8064ffffffffff0482111561488857614888613f87565b029392505050565b5f612ee364ffffffffff841664ffffffffff84166147bd565b64ffffffffff81811683821602908116908181146148c9576148c9613f87565b509291505056fea164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102b8575f3560e01c80639a7a2ffc11610177578063da881e5a116100d5578063ebf0c7171161008f578063ebf0c71714610694578063f16505361461069c578063f2fde38b146106b6578063f379b0df146106c9578063f52fd80314610703578063f6fc05d514610773578063fa9325691461077c575f5ffd5b8063da881e5a14610623578063dbb06c9314610636578063e4be6e3d14610648578063e59e46951461065b578063e6745e131461066e578063e82f3b7014610681575f5ffd5b8063b8ab470411610131578063b8ab470414610584578063bff232c1146105a6578063c2b40ae4146105b9578063c3a0ec30146105d8578063c8fe182d146105e9578063ca2869a0146105f1578063cd6dc68714610610575f5ffd5b80639a7a2ffc146104f05780639f0f874a1461052c578063a016493014610535578063a8a4d69b14610555578063acc5249414610568578063b2d5d1ac1461057b575f5ffd5b806350e6fad31161022457806385814243116101de57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ac5780638da5cb5b146104c25780638e5ce3ad146104ca5780639015d371146104dd575f5ffd5b806350e6fad3146103e25780635d504776146103ec57806362cc89a8146103ff57806370e36bbe1461041f578063715018a6146104325780637c92f5241461043a575f5ffd5b80632800d829116102755780632800d82914610351578063291a691b146103645780632e7b716d14610387578063323beaa51461039a5780634d6861a6146103ad57806350e6d94c146103c0575f5ffd5b8063096b810a146102bc578063099a161a146102d15780630b45f8e9146102f75780630bbfade71461030a5780630f3e34121461031d57806317d6112014610330575b5f5ffd5b6102cf6102ca366004613c75565b61078f565b005b6102e46102df366004613c90565b6108db565b6040519081526020015b60405180910390f35b6102cf610305366004613c75565b610914565b6102cf610318366004613ceb565b6109b5565b6102cf61032b366004613c90565b610bf9565b61034361033e366004613c90565b610c3c565b6040516102ee929190613e0c565b6102e461035f366004613c90565b610de5565b610377610372366004613e39565b610e31565b60405190151581526020016102ee565b610377610395366004613c75565b61100b565b6102cf6103a8366004613c75565b6110be565b6103776103bb366004613c90565b6111cf565b6103776103ce366004613c75565b60066020525f908152604090205460ff1681565b6102e46202a30081565b6103776103fa366004613e72565b61120e565b600d54610412906001600160a01b031681565b6040516102ee9190613ea0565b6102cf61042d366004613c75565b611251565b6102cf6112c7565b61044d610448366004613eb4565b6112da565b6040805192835263ffffffff9091166020830152016102ee565b600154610412906001600160a01b031681565b6102cf610488366004613c75565b611481565b6102e461049b366004613c90565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102e4565b6104126115bf565b600b54610412906001600160a01b031681565b6103776104eb366004613c75565b6115ed565b6105166104fe366004613c75565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ee565b6102e460035481565b610548610543366004613c90565b61160a565b6040516102ee9190613ee9565b610377610563366004613e72565b6116a0565b6102cf610576366004613c75565b6116e3565b6102e4600e5481565b610597610592366004613c90565b61177a565b6040516102ee93929190613efb565b6102cf6105b4366004613c75565b6118c5565b6102e46105c7366004613c90565b60086020525f908152604090205481565b6001546001600160a01b0316610412565b6102cf61193d565b6102e46105ff366004613c90565b5f9081526008602052604090205490565b6102cf61061e366004613f3d565b6119b9565b610377610631366004613c90565b611b16565b5f54610412906001600160a01b031681565b600c54610412906001600160a01b031681565b6102cf610669366004613c75565b611df9565b6102cf61067c366004613f67565b611e71565b6102e461068f366004613c90565b612034565b6102e4612065565b6106a4601481565b60405160ff90911681526020016102ee565b6102cf6106c4366004613c75565b612077565b6004546106e59064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ee565b610744610711366004613c90565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ee949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e460025481565b61041261078a366004613f67565b6120b1565b6107976115bf565b6001600160a01b0316336001600160a01b031614806107c057506001546001600160a01b031633145b6107dd57604051632864c4e160e01b815260040160405180910390fd5b6107e6816115ed565b819061080f576040516381e5828960e01b81526004016108069190613ea0565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff169061083d906004908361211e565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161086a83613f9b565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a60205260408120600481015461090a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61091c6123c0565b600c546001600160a01b0316156109455760405162035f5560e61b815260040160405180910390fd5b6001600160a01b03811661096c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383169081179091556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b905f90a250565b5f888152600a602052604090206002815460ff1660038111156109da576109da613fb0565b146109f857604051634f4b461f60e11b815260040160405180910390fd5b600481015415610a1b5760405163632a22bb60e01b815260040160405180910390fd5b85610a3957604051636caad1ed60e11b815260040160405180910390fd5b5f610a9d82600601805480602002602001604051908101604052809291908181526020018280548015610a9357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610a75575b50505050506123f2565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610aef573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b169190810190614140565b9050806101c0015115610b3357610b338b828a858b8b8b8b6124f8565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610b91575f5ffd5b505af1158015610ba3573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610be496959493929190614302565b60405180910390a25050505050505050505050565b610c016123c0565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610c7257610c72613fc4565b604051908082528060200260200182016040528015610c9b578160200160208202803683370190505b509450806001600160401b03811115610cb657610cb6613fc4565b604051908082528060200260200182016040528015610cdf578160200160208202803683370190505b5093505f805b83811015610ddb575f856006018281548110610d0357610d0361434f565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610d4957610d49613fb0565b03610dd25780888481518110610d6157610d6161434f565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610db957610db961434f565b602090810291909101015282610dce81614363565b9350505b50600101610ce5565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610e0957610e09613fb0565b03610e2757604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610e5c5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610e8057610e80613fb0565b14610e9e576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610ee5573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f09919061437b565b905080610f1c60408601602087016143a5565b63ffffffff161115610f3460408601602087016143a5565b829091610f62576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610806565b5050815460ff1916600190811783558201859055436002830155600354610f8990426143be565b6003830155610f9d60058301856002613b73565b50610fa6612065565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ff7928a928a92916143d1565b60405180910390a250600195945050505050565b5f611015826115ed565b61102057505f919050565b6001546001600160a01b0316611049576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790611079908590600401613ea0565b602060405180830381865afa158015611094573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b89190614421565b92915050565b6110c66123c0565b600d546001600160a01b0316806110f05760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461113157604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610806565b50505f6202a300600e5461114591906143be565b9050804281811015611173576040516337c8270b60e01b815260048101929092526024820152604401610806565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690555f600e8190556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b9190a2505050565b5f818152600a602052604081206001815460ff1660038111156111f4576111f4613fb0565b1461120157505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561124957611249613fb0565b149392505050565b6112596123c0565b6001600160a01b0381166112805760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6112cf6123c0565b6112d85f6126f4565b565b600b545f9081906001600160a01b031633146113095760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561132e5761132e613fb0565b1461134c57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561138c5761138c613fb0565b1461139c57600b01549150611479565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916113d083613f9b565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611421929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6114896115bf565b6001600160a01b0316336001600160a01b031614806114b257506001546001600160a01b031633145b6114cf57604051632864c4e160e01b815260040160405180910390fd5b6114d8816115ed565b6115bc5760048054600160281b900464ffffffffff1690611502906001600160a01b038416612764565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161155383614363565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016108cf565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a6020526040902060048101546060919061163d576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561169357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611675575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156116da576116da613fb0565b14159392505050565b6116eb6123c0565b6001600160a01b0381166117125760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611766906202a300906143be565b60405190815260200160405180910390a250565b5f81815260096020526040902054606090819081906117ac576040516322e679e360e11b815260040160405180910390fd5b5f848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561181157602002820191905f5260205f20905b8154815260200190600101908083116117fd575b505050505092508180548060200260200160405190810160405280929190818152602001828054801561186157602002820191905f5260205f20905b81548152602001906001019080831161184d575b50505050509150808054806020026020016040519081016040528092919081815260200182805480156118b157602002820191905f5260205f20905b81548152602001906001019080831161189d575b505050505090509250925092509193909250565b6118cd6123c0565b6001600160a01b0381166118f45760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6119456123c0565b600d546001600160a01b03168061196f5760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690555f600e8190556040516001600160a01b038316917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a250565b5f6119c261293a565b805490915060ff600160401b82041615906001600160401b03165f811580156119e85750825b90505f826001600160401b03166001148015611a035750303b155b905081158015611a11575080155b15611a2f5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611a5957845460ff60401b1916600160401b1785555b6001600160a01b038716611a805760405163d92e233d60e01b815260040160405180910390fd5b611a8933612962565b611a9560046014612973565b611a9e86610bf9565b611aa66115bf565b6001600160a01b0316876001600160a01b031614611ac757611ac787612077565b8315611b0d57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff166003811115611b3a57611b3a613fb0565b03611b5857604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611b7057611b70613fb0565b14611b8e57604051631860f69960e31b815260040160405180910390fd5b80600301544211611bb257604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611c97578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611c78575f5ffd5b505af1158015611c8a573d5f5f3e3d5ffd5b505f979650505050505050565b611ca0826129f2565b815460ff191660021782556006820154600b83018190555f816001600160401b03811115611cd057611cd0613fc4565b604051908082528060200260200182016040528015611cf9578160200160208202803683370190505b5090505f5b82811015611d6b57846009015f866006018381548110611d2057611d2061434f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611d5857611d5861434f565b6020908102919091010152600101611cfe565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611dae575f5ffd5b505af1158015611dc0573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ff792919061443a565b611e016123c0565b6001600160a01b038116611e285760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611e9557611e95613fb0565b03611eb357604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611ecb57611ecb613fb0565b14611ee957604051631860f69960e31b815260040160405180910390fd5b8060030154421115611f0e57604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611f405760405163257309f160e11b815260040160405180910390fd5b611f493361100b565b611f665760405163149fbcfd60e11b815260040160405180910390fd5b611f71338385612b18565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611ff090839083612ce9565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480612060576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61207260046014612eea565b905090565b61207f6123c0565b6001600160a01b0381166120a8575f604051631e4fbdf760e01b81526004016108069190613ea0565b6115bc816126f4565b5f828152600a60205260408120600601805483908082106120ee576040516326c5c55b60e11b815260048101929092526024820152604401610806565b50508083815481106121025761210261434f565b5f918252602090912001546001600160a01b0316949350505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061215d5760405162461bcd60e51b81526004016108069061444c565b825464ffffffffff600160281b909104811690821681116121bb5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610806565b825f5b81866001015f6121ce8488612fe3565b64ffffffffff1681526020019081526020015f20819055505f8160016121f49190614496565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff16811161222957506123b8565b600185165f036122f0575f612248836122438860016144af565b612fe3565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916122a9916004016144cc565b602060405180830381865af41580156122c4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e8919061437b565b9350506123a4565b5f612300836122436001896144fc565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612361916004016144cc565b602060405180830381865af415801561237c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123a0919061437b565b9350505b50647fffffffff600194851c1693016121be565b505050505050565b336123c96115bf565b6001600160a01b0316146112d8573360405163118cdaa760e01b81526004016108069190613ea0565b80515f9081612402826014614519565b6001600160401b0381111561241957612419613fc4565b6040519080825280601f01601f191660200182016040528015612443576020820181803683370190505b5090505f5b828110156124e8575f8582815181106124635761246361434f565b602002602001015160601b90505f82601461247e9190614519565b90505f5b60148110156124da5782816014811061249d5761249d61434f565b1a60f81b856124ac83856143be565b815181106124bc576124bc61434f565b60200101906001600160f81b03191690815f1a905350600101612482565b505050806001019050612448565b5080516020909101209392505050565b8261251657604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b815260040161254d9493929190614530565b602060405180830381865afa158015612568573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061258c9190614421565b6125a95760405163051d8aa760e51b815260040160405180910390fd5b806125c757604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b03166125f057604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b815260040161263f979695949392919061454f565b5f60405180830381865afa158015612659573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526126809190810190614629565b5f8e8152600f6020908152604090912084519497509295509093506126a89290860190613c14565b505f8b815260106020908152604090912083516126c792850190613c14565b505f8b815260116020908152604090912082516126e692840190613c14565b505050505050505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106127b35760405162461bcd60e51b81526004016108069061444c565b825464ffffffffff908116908216106128065760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610806565b6128118160016144af565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6128488487612fe3565b64ffffffffff16815260208101919091526040015f20556001831615612933575f612878826122436001876144fc565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916128d9916004016144cc565b602060405180830381865af41580156128f4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612918919061437b565b647fffffffff600195861c1694909350919091019050612838565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006110b8565b61296a613000565b6115bc81613025565b602060ff821611156129c15760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610806565b6129d2600160ff831681901b61470d565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612b13575f612a0d8260016143be565b90505b82811015612b0a575f846006018381548110612a2e57612a2e61434f565b5f9182526020822001546006870180546001600160a01b0390921693509084908110612a5c57612a5c61434f565b5f918252602090912001546001600160a01b0390811691508216811015612b005780866006018581548110612a9357612a9361434f565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612ad457612ad461434f565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612a10565b506001016129f9565b505050565b5f8211612b385760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612b61576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612b979161470d565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612bde573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c02919061437b565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c55573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c79919061437b565b90505f8111612c9b5760405163aeaddff160e01b815260040160405180910390fd5b5f612ca68284614720565b90505f8111612cc85760405163149fbcfd60e11b815260040160405180910390fd5b80861115611b0d5760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612d6757508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612ee3565b5f5f90505f876009015f855f81548110612d8357612d8361434f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612e0b575f896009015f878481548110612dcd57612dcd61434f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e02578092508193505b50600101612dac565b50808610612e1f575f945050505050612ee3565b5f88600a015f868581548110612e3757612e3761434f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612e7457612e74613fb0565b021790555086848381548110612e8c57612e8c61434f565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612f3d5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610806565b602060ff83161115612f615760405162461bcd60e51b81526004016108069061473f565b8254600160281b900464ffffffffff1680612f8060ff85166002614890565b64ffffffffff161015612fd05760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610806565b612fdb84828561302d565b949350505050565b5f81612ff660ff851663ffffffff6148a9565b612ee391906144af565b6130086130f5565b6112d857604051631afcd79f60e31b815260040160405180910390fd5b61207f613000565b5f602060ff831611156130525760405162461bcd60e51b81526004016108069061473f565b8264ffffffffff165f03613070576130698261310e565b9050612ee3565b5f61307c836001614496565b60ff166001600160401b0381111561309657613096613fc4565b6040519080825280602002602001820160405280156130bf578160200160208202803683370190505b5090506130ce858585846137a8565b808360ff16815181106130e3576130e361434f565b60200260200101519150509392505050565b5f6130fe61293a565b54600160401b900460ff16919050565b5f8160ff165f0361312057505f919050565b8160ff1660010361315257507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361318457507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036131b657507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036131e857507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361321a57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361324c57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361327e57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036132b057507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036132e257507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361331457507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361334657507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361337857507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036133aa57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036133dc57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361340e57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361344057507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361347257507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff166012036134a457507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036134d657507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361350857507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361353a57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361356c57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361359e57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036135d057507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361360257507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361363457507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361366657507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361369857507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036136ca57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036136fc57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361372e57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361376057507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610806565b602060ff831611156137cc5760405162461bcd60e51b81526004016108069061473f565b5f8364ffffffffff16116138305760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610806565b5f61383c6001856144fc565b9050600181165f0361388f57846001015f6138575f84612fe3565b64ffffffffff1681526020019081526020015f2054825f8151811061387e5761387e61434f565b6020026020010181815250506138b7565b6138985f61310e565b825f815181106138aa576138aa61434f565b6020026020010181815250505b5f5b8360ff168160ff1610156123b857600182165f036139af5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061390b5761390b61434f565b602002602001015181526020016139218561310e565b8152506040518263ffffffff1660e01b815260040161394091906144cc565b602060405180830381865af415801561395b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061397f919061437b565b8361398b836001614496565b60ff168151811061399e5761399e61434f565b602002602001018181525050613b60565b5f6139bb826001614496565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613a5d575f876001015f613a12856001613a019190614496565b60018864ffffffffff16901c612fe3565b64ffffffffff1681526020019081526020015f205490508085846001613a389190614496565b60ff1681518110613a4b57613a4b61434f565b60200260200101818152505050613b5e565b5f876001015f613a748560018861224391906144fc565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613acb57613acb61434f565b60200260200101518152506040518263ffffffff1660e01b8152600401613af291906144cc565b602060405180830381865af4158015613b0d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b31919061437b565b85613b3d856001614496565b60ff1681518110613b5057613b5061434f565b602002602001018181525050505b505b647fffffffff600192831c1691016138b9565b600183019183908215613c04579160200282015f5b83821115613bd257833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613b88565b8015613c025782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613bd2565b505b50613c10929150613c4d565b5090565b828054828255905f5260205f20908101928215613c04579160200282015b82811115613c04578251825591602001919060010190613c32565b5b80821115613c10575f8155600101613c4e565b6001600160a01b03811681146115bc575f5ffd5b5f60208284031215613c85575f5ffd5b8135612ee381613c61565b5f60208284031215613ca0575f5ffd5b5035919050565b5f5f83601f840112613cb7575f5ffd5b5081356001600160401b03811115613ccd575f5ffd5b602083019150836020828501011115613ce4575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613d02575f5ffd5b8835975060208901356001600160401b03811115613d1e575f5ffd5b613d2a8b828c01613ca7565b9098509650506040890135945060608901356001600160401b03811115613d4f575f5ffd5b613d5b8b828c01613ca7565b90955093505060808901356001600160401b03811115613d79575f5ffd5b613d858b828c01613ca7565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613dd25781516001600160a01b0316865260209586019590910190600101613dab565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613dd2578151865260209586019590910190600101613dee565b604081525f613e1e6040830185613d99565b8281036020840152613e308185613ddc565b95945050505050565b5f5f5f60808486031215613e4b575f5ffd5b833592506020840135915060808401851015613e65575f5ffd5b6040840190509250925092565b5f5f60408385031215613e83575f5ffd5b823591506020830135613e9581613c61565b809150509250929050565b6001600160a01b0391909116815260200190565b5f5f5f60608486031215613ec6575f5ffd5b833592506020840135613ed881613c61565b929592945050506040919091013590565b602081525f612ee36020830184613d99565b606081525f613f0d6060830186613ddc565b8281036020840152613f1f8186613ddc565b90508281036040840152613f338185613ddc565b9695505050505050565b5f5f60408385031215613f4e575f5ffd5b8235613f5981613c61565b946020939093013593505050565b5f5f60408385031215613f78575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613fa957613fa9613f87565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613ffb57613ffb613fc4565b60405290565b604051601f8201601f191681016001600160401b038111828210171561402957614029613fc4565b604052919050565b805160048110612060575f5ffd5b5f82601f83011261404e575f5ffd5b604080519081016001600160401b038111828210171561407057614070613fc4565b8060405250806040840185811115614086575f5ffd5b845b818110156140a0578051835260209283019201614088565b509195945050505050565b805161206081613c61565b805160ff81168114612060575f5ffd5b5f82601f8301126140d5575f5ffd5b81516001600160401b038111156140ee576140ee613fc4565b614101601f8201601f1916602001614001565b818152846020838601011115614115575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114612060575f5ffd5b5f60208284031215614150575f5ffd5b81516001600160401b03811115614165575f5ffd5b82016102008185031215614177575f5ffd5b61417f613fd8565b8151815261418f60208301614031565b6020820152604082810151908201526141ab856060840161403f565b606082015260a082015160808201526141c660c083016140ab565b60a08201526141d760e083016140b6565b60c08201526101008201516001600160401b038111156141f5575f5ffd5b614201868285016140c6565b60e08301525061421461012083016140ab565b61010082015261422761014083016140ab565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561425d575f5ffd5b614269868285016140c6565b6101808301525061427d6101c083016140ab565b6101a08201526142906101e08301614131565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613dd25781546001600160a01b03168652602090950194600191820191016142b3565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f614314608083018961429e565b828103602084015261432781888a6142da565b905085604084015282810360608401526143428185876142da565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161437457614374613f87565b5060010190565b5f6020828403121561438b575f5ffd5b5051919050565b803563ffffffff81168114612060575f5ffd5b5f602082840312156143b5575f5ffd5b612ee382614392565b808201808211156110b8576110b8613f87565b84815260a0810160208201855f5b600281101561440c5763ffffffff6143f683614392565b16835260209283019291909101906001016143df565b50505060608201939093526080015292915050565b5f60208284031215614431575f5ffd5b612ee382614131565b604081525f613e1e604083018561429e565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff81811683821601908111156110b8576110b8613f87565b64ffffffffff81811683821601908111156110b8576110b8613f87565b6040810181835f5b60028110156144f35781518352602092830192909101906001016144d4565b50505092915050565b64ffffffffff82811682821603908111156110b8576110b8613f87565b80820281158282048414176110b8576110b8613f87565b848152836020820152606060408201525f613f336060830184866142da565b60018060a01b038816815286602082015285604082015260a060608201525f61457c60a0830186886142da565b828103608084015261458f8185876142da565b9a9950505050505050505050565b5f6001600160401b038211156145b5576145b5613fc4565b5060051b60200190565b5f82601f8301126145ce575f5ffd5b81516145e16145dc8261459d565b614001565b8082825260208201915060208360051b860101925085831115614602575f5ffd5b602085015b8381101561461f578051835260209283019201614607565b5095945050505050565b5f5f5f6060848603121561463b575f5ffd5b83516001600160401b03811115614650575f5ffd5b8401601f81018613614660575f5ffd5b805161466e6145dc8261459d565b8082825260208201915060208360051b85010192508883111561468f575f5ffd5b6020840193505b828410156146b1578351825260209384019390910190614696565b8096505050505060208401516001600160401b038111156146d0575f5ffd5b6146dc868287016145bf565b92505060408401516001600160401b038111156146f7575f5ffd5b614703868287016145bf565b9150509250925092565b818103818111156110b8576110b8613f87565b5f8261473a57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115611479578085048111156147a1576147a1613f87565b60018416156147af57908102905b60019390931c928002614786565b5f826147cb575060016110b8565b816147d757505f6110b8565b81600181146147ed57600281146147f757614829565b60019150506110b8565b60ff84111561480857614808613f87565b6001841b915064ffffffffff82111561482357614823613f87565b506110b8565b5060208310610133831016604e8410600b8410161715614861575081810a64ffffffffff81111561485c5761485c613f87565b6110b8565b61487164ffffffffff8484614782565b8064ffffffffff0482111561488857614888613f87565b029392505050565b5f612ee364ffffffffff841664ffffffffff84166147bd565b64ffffffffff81811683821602908116908181146148c9576148c9613f87565b509291505056fea164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 8271 + "start": 9049 }, { "length": 20, - "start": 8455 + "start": 9233 }, { "length": 20, - "start": 9855 + "start": 10633 }, { "length": 20, - "start": 13983 + "start": 14761 }, { "length": 20, - "start": 14425 + "start": 15203 } ] } @@ -1470,28 +1629,28 @@ "PoseidonT3": [ { "length": 20, - "start": 8057 + "start": 8835 }, { "length": 20, - "start": 8241 + "start": 9019 }, { "length": 20, - "start": 9641 + "start": 10419 }, { "length": 20, - "start": 13769 + "start": 14547 }, { "length": 20, - "start": 14211 + "start": 14989 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-369f1ad8eea78d4c2afd75f88d1b51906ad9ac6c" + "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index 71b316260..4f6708283 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -150,6 +150,20 @@ interface ICiphernodeRegistry { /// @param enclave Address of the enclave contract. event EnclaveSet(address indexed enclave); + /// @notice Emitted when the owner proposes a new DKG fold-attestation verifier. + /// @dev The new verifier becomes active only after `commitDkgFoldAttestationVerifier` + /// is called and at least `DKG_FOLD_VERIFIER_TIMELOCK` has elapsed. + event DkgFoldAttestationVerifierProposed( + address indexed verifier, + uint256 readyAt + ); + + /// @notice Emitted when the proposed DKG fold-attestation verifier becomes active. + event DkgFoldAttestationVerifierUpdated(address indexed verifier); + + /// @notice Emitted when the owner cancels a pending verifier proposal. + event DkgFoldAttestationVerifierProposalCancelled(address indexed verifier); + /// @notice This event MUST be emitted when a ciphernode is added to the registry. /// @param node Address of the ciphernode. /// @param index Index of the ciphernode in the registry. @@ -226,6 +240,11 @@ interface ICiphernodeRegistry { /// @notice `dkgFoldAttestationVerifier` is not configured on the registry error FoldAttestationVerifierNotSet(); + /// @notice Initial verifier set was attempted after one was already configured. + /// @dev Subsequent changes must go through `proposeDkgFoldAttestationVerifier` → + /// `commitDkgFoldAttestationVerifier` (timelocked). + error FoldAttestationVerifierAlreadySet(); + /// @notice Fold attestation bundle failed signature or public-input binding checks error InvalidFoldAttestation(); @@ -238,6 +257,16 @@ interface ICiphernodeRegistry { /// @notice `partyId` is out of bounds for the finalized committee's `topNodes` error PartyIdOutOfBounds(uint256 partyId, uint256 committeeSize); + /// @notice A `setDkgFoldAttestationVerifier` commit was attempted before the timelock elapsed. + error VerifierUpdateTimelockActive(uint256 readyAt, uint256 nowAt); + + /// @notice `commitDkgFoldAttestationVerifier` was called but no proposal is pending. + error NoPendingVerifierUpdate(); + + /// @notice `commitDkgFoldAttestationVerifier` was called with an address that does + /// not match the pending proposal (prevents commit-time substitution). + error VerifierMismatch(address pending, address provided); + /// @notice Node has already submitted a ticket for this E3 error NodeAlreadySubmitted(); diff --git a/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol b/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol index a0d406e61..9a703a381 100644 --- a/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol +++ b/packages/enclave-contracts/contracts/lib/DkgFoldAttestationLib.sol @@ -7,22 +7,37 @@ pragma solidity 0.8.28; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import { - MessageHashUtils -} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; /** * @title DkgFoldAttestationLib - * @notice EIP-712-style digests for per-node DKG fold attestations. - * @dev Must stay aligned with `DkgFoldAttestationPayload::typehash()` in + * @notice Canonical EIP-712 digests for per-node DKG fold attestations. + * @dev Must stay aligned with `DkgFoldAttestationPayload` in * `crates/events/src/enclave_event/dkg_fold_attestation.rs`. + * + * Domain binds `chainId` and `verifyingContract` (the + * `DkgFoldAttestationVerifier` address); the struct binds `e3Id`, + * `partyId`, and the two NodeFold commitments. Signatures cannot be + * replayed across chains, verifier deployments, or E3s. */ library DkgFoldAttestationLib { - /// @dev `keccak256("DkgFoldAttestation(uint256 chainId,uint256 e3Id, - /// uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)")` + /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")` + bytes32 public constant EIP712_DOMAIN_TYPEHASH = + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + + /// @dev `keccak256("EnclaveDkgFoldAttestation")` + bytes32 public constant DOMAIN_NAME_HASH = + keccak256(bytes("EnclaveDkgFoldAttestation")); + + /// @dev `keccak256("1")` + bytes32 public constant DOMAIN_VERSION_HASH = keccak256(bytes("1")); + + /// @dev `keccak256("DkgFoldAttestation(uint256 e3Id,uint256 partyId, + /// bytes32 skAggCommit,bytes32 esmAggCommit)")` bytes32 public constant TYPEHASH = keccak256( - "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)" + "DkgFoldAttestation(uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)" ); /// @notice One node's signed fold output. @@ -39,9 +54,40 @@ library DkgFoldAttestationLib { address node; } - /// @notice `personal_sign` digest for a fold attestation (EIP-191 applied by callers). - function digest( + /// @notice EIP-712 domain separator bound to `chainId` and `verifyingContract`. + function domainSeparator( + uint256 chainId, + address verifyingContract + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + EIP712_DOMAIN_TYPEHASH, + DOMAIN_NAME_HASH, + DOMAIN_VERSION_HASH, + chainId, + verifyingContract + ) + ); + } + + /// @notice `hashStruct(DkgFoldAttestation)` per EIP-712. + function structHash( + uint256 e3Id, + uint256 partyId, + bytes32 skAggCommit, + bytes32 esmAggCommit + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode(TYPEHASH, e3Id, partyId, skAggCommit, esmAggCommit) + ); + } + + /// @notice EIP-712 typed-data hash: `keccak256("\x19\x01" || domainSeparator || structHash)`. + function typedDataHash( uint256 chainId, + address verifyingContract, uint256 e3Id, uint256 partyId, bytes32 skAggCommit, @@ -49,33 +95,29 @@ library DkgFoldAttestationLib { ) internal pure returns (bytes32) { return keccak256( - abi.encode( - TYPEHASH, - chainId, - e3Id, - partyId, - skAggCommit, - esmAggCommit + abi.encodePacked( + "\x19\x01", + domainSeparator(chainId, verifyingContract), + structHash(e3Id, partyId, skAggCommit, esmAggCommit) ) ); } - /// @notice Recover the signer for a fold attestation. + /// @notice Recover the EIP-712 signer for a fold attestation. function recoverSigner( uint256 chainId, + address verifyingContract, uint256 e3Id, Attestation memory attestation ) internal pure returns (address) { - bytes32 structHash = digest( + bytes32 digest = typedDataHash( chainId, + verifyingContract, e3Id, attestation.partyId, attestation.skAggCommit, attestation.esmAggCommit ); - bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash( - structHash - ); - return ECDSA.recover(ethSignedHash, attestation.signature); + return ECDSA.recover(digest, attestation.signature); } } diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 5b56f93c1..4321d28c7 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -91,6 +91,17 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { /// @notice Verifies per-node DKG fold attestations at publication (external contract). IDkgFoldAttestationVerifier public dkgFoldAttestationVerifier; + /// @notice Minimum delay between proposing a verifier change and committing it. + /// @dev Treats `dkgFoldAttestationVerifier` as a critical admin key: a compromised + /// owner cannot instantly swap it for a weak verifier that bypasses + /// per-party attestation checks; the proposal is visible on-chain for + /// this window and can be cancelled by the (recovered) legitimate owner. + uint256 public constant DKG_FOLD_VERIFIER_TIMELOCK = 2 days; + + /// @notice Pending verifier proposal awaiting commit. `pendingAt == 0` means no proposal. + address public pendingDkgFoldAttestationVerifier; + uint256 public pendingDkgFoldAttestationVerifierAt; + /// @notice DKG anchor commitments stored when the committee public key is published. mapping(uint256 e3Id => uint256[]) internal dkgPartyIds; mapping(uint256 e3Id => bytes32[]) internal dkgSkAggCommits; @@ -286,12 +297,70 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { dkgEsmAggCommits[e3Id] = esmAgg; } - /// @notice Sets the DKG fold attestation verifier (required when proof aggregation is enabled). - function setDkgFoldAttestationVerifier( + /// @notice Propose a new DKG fold-attestation verifier. The change becomes active + /// only after `DKG_FOLD_VERIFIER_TIMELOCK` has elapsed and `commitDkgFoldAttestationVerifier` + /// is called. Replaces any pending proposal. + /// @dev First-time set is also subject to the timelock — operators must wait + /// the same window before the verifier is active. For the deploy-time + /// initial set, see `setInitialDkgFoldAttestationVerifier`. + function proposeDkgFoldAttestationVerifier( IDkgFoldAttestationVerifier verifier ) external onlyOwner { + require(address(verifier) != address(0), ZeroAddress()); + pendingDkgFoldAttestationVerifier = address(verifier); + pendingDkgFoldAttestationVerifierAt = block.timestamp; + emit DkgFoldAttestationVerifierProposed( + address(verifier), + block.timestamp + DKG_FOLD_VERIFIER_TIMELOCK + ); + } + + /// @notice Commit a previously proposed verifier change after the timelock elapses. + /// @param verifier Must match the pending proposal (prevents commit-time substitution). + function commitDkgFoldAttestationVerifier( + IDkgFoldAttestationVerifier verifier + ) external onlyOwner { + address pending = pendingDkgFoldAttestationVerifier; + require(pending != address(0), NoPendingVerifierUpdate()); + require( + pending == address(verifier), + VerifierMismatch(pending, address(verifier)) + ); + uint256 readyAt = pendingDkgFoldAttestationVerifierAt + + DKG_FOLD_VERIFIER_TIMELOCK; + require( + block.timestamp >= readyAt, + VerifierUpdateTimelockActive(readyAt, block.timestamp) + ); + dkgFoldAttestationVerifier = verifier; + pendingDkgFoldAttestationVerifier = address(0); + pendingDkgFoldAttestationVerifierAt = 0; + emit DkgFoldAttestationVerifierUpdated(address(verifier)); + } + + /// @notice Cancel a pending verifier proposal. + function cancelDkgFoldAttestationVerifierProposal() external onlyOwner { + address pending = pendingDkgFoldAttestationVerifier; + require(pending != address(0), NoPendingVerifierUpdate()); + pendingDkgFoldAttestationVerifier = address(0); + pendingDkgFoldAttestationVerifierAt = 0; + emit DkgFoldAttestationVerifierProposalCancelled(pending); + } + + /// @notice One-shot initial set, allowed only when no verifier has ever been configured. + /// @dev Lets deploy scripts wire the verifier without first waiting the timelock. + /// Subsequent changes must go through `propose`/`commit`. Cannot be used to + /// bypass the timelock for replacement — only for the very first set. + function setInitialDkgFoldAttestationVerifier( + IDkgFoldAttestationVerifier verifier + ) external onlyOwner { + require( + address(dkgFoldAttestationVerifier) == address(0), + FoldAttestationVerifierAlreadySet() + ); require(address(verifier) != address(0), ZeroAddress()); dkgFoldAttestationVerifier = verifier; + emit DkgFoldAttestationVerifierUpdated(address(verifier)); } /// @inheritdoc ICiphernodeRegistry diff --git a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol index b5068af4f..e82b549ce 100644 --- a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol +++ b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol @@ -87,6 +87,10 @@ contract SlashingManager is ISlashingManager, AccessControl { /// @notice EIP-712 style typehash for committee attestation votes. /// @dev Must match `AccusationManager::vote_digest()` in `crates/zk-prover/src/actors/accusation_manager.rs`. /// Includes chainId to prevent cross-chain replay and dataHash for equivocation detection. + /// @dev Signature scheme uses EIP-191 (`personal_sign`) wrapping rather than a + /// full EIP-712 domain separator with `verifyingContract`; canonicalising the + /// domain is tracked as a follow-up (requires plumbing the SlashingManager + /// address into off-chain signers). bytes32 public constant VOTE_TYPEHASH = keccak256( "AccusationVote(uint256 chainId,uint256 e3Id," @@ -164,12 +168,16 @@ contract SlashingManager is ISlashingManager, AccessControl { InvalidPolicy() ); - if (policy.requiresProof) { - // Attestation-based slashes: no proofVerifier needed, no appeal window - require(policy.appealWindow == 0, InvalidPolicy()); - } else { + if (!policy.requiresProof) { + // Lane B: evidence-based slashes must give the accused an appeal window. require(policy.appealWindow > 0, InvalidPolicy()); } + // Lane A (requiresProof=true): `appealWindow` is the execution-delay + // window during which the accused can file an appeal before the slash + // executes. `0` means execute immediately (preserves backwards-compatible + // behavior for policies that opt out of recourse), but governance is + // free to set a non-zero window for added recourse on attestation-based + // slashes. slashPolicies[reason] = policy; emit SlashPolicyUpdated(reason, policy); @@ -232,7 +240,11 @@ contract SlashingManager is ISlashingManager, AccessControl { /// @inheritdoc ISlashingManager /// @dev Lane A: Permissionless committee attestation-based slash. Anyone can call. - /// Atomically proposes, verifies committee vote signatures, and executes slash. + /// Proposes the slash and verifies committee vote signatures. Execution + /// timing depends on the policy's `appealWindow`: + /// - `appealWindow == 0`: execute atomically in this call (no recourse). + /// - `appealWindow > 0`: defer; the accused has `appealWindow` seconds + /// to `fileAppeal`, after which anyone can call `executeSlash(proposalId)`. /// The slash reason is derived deterministically from proofType as /// `keccak256(abi.encodePacked(proofType))`, eliminating the need for the caller /// to pass a reason and preventing cross-reason replay attacks. @@ -281,6 +293,8 @@ contract SlashingManager is ISlashingManager, AccessControl { proposalId = totalProposals; totalProposals = proposalId + 1; + uint256 executableAt = block.timestamp + policy.appealWindow; + SlashProposal storage p = _proposals[proposalId]; p.e3Id = e3Id; p.operator = operator; @@ -288,7 +302,7 @@ contract SlashingManager is ISlashingManager, AccessControl { p.ticketAmount = policy.ticketPenalty; p.licenseAmount = policy.licensePenalty; p.proposedAt = block.timestamp; - p.executableAt = block.timestamp; + p.executableAt = executableAt; p.proposer = msg.sender; p.proofHash = keccak256(proof); p.proofVerified = true; @@ -303,11 +317,17 @@ contract SlashingManager is ISlashingManager, AccessControl { reason, policy.ticketPenalty, policy.licensePenalty, - block.timestamp, + executableAt, msg.sender ); - _executeSlash(proposalId); + // If `appealWindow == 0` (no recourse), execute atomically as before. + // Otherwise, wait for the appeal window: the accused can call + // `fileAppeal` before `executableAt`, then anyone (or governance, + // after appeal resolution) can call `executeSlash(proposalId)`. + if (policy.appealWindow == 0) { + _executeSlash(proposalId); + } } /// @inheritdoc ISlashingManager @@ -364,16 +384,17 @@ contract SlashingManager is ISlashingManager, AccessControl { } /// @inheritdoc ISlashingManager - /// @dev Only for evidence-based slashes (Lane B). Proof-based slashes execute atomically. + /// @dev Used for: + /// - Lane B (evidence-based) slashes after the appeal window. + /// - Lane A (proof-based) slashes whose policy has a non-zero + /// `appealWindow`. Lane A policies with `appealWindow == 0` are + /// already executed atomically inside `proposeSlash`. function executeSlash(uint256 proposalId) external { require(proposalId < totalProposals, InvalidProposal()); SlashProposal storage p = _proposals[proposalId]; require(!p.executed, AlreadyExecuted()); - // Use snapshotted requiresProof state: proof-based slashes are already executed atomically in proposeSlash - require(!p.proofVerified, InvalidPolicy()); - - // Evidence mode: check appeal window + // Both lanes share the same appeal-window machinery now. require(block.timestamp >= p.executableAt, AppealWindowActive()); if (p.appealed) { require(p.resolved, AppealPending()); @@ -607,12 +628,12 @@ contract SlashingManager is ISlashingManager, AccessControl { // Only the accused can appeal require(msg.sender == p.operator, Unauthorized()); + // Already-executed slashes (Lane A with appealWindow == 0) cannot be appealed. + require(!p.executed, AlreadyExecuted()); // Only within the appeal window require(block.timestamp < p.executableAt, AppealWindowExpired()); // Only once require(!p.appealed, AlreadyAppealed()); - // Cannot appeal proof-verified slashes (they have no appeal window) - require(!p.proofVerified, InvalidProposal()); p.appealed = true; diff --git a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol index 45b55ed77..0b629fb42 100644 --- a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol @@ -173,6 +173,7 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { address signer = DkgFoldAttestationLib.recoverSigner( chainId, + address(this), e3Id, att ); diff --git a/packages/enclave-contracts/scripts/deployEnclave.ts b/packages/enclave-contracts/scripts/deployEnclave.ts index ba73d0d8e..c73b94c3c 100644 --- a/packages/enclave-contracts/scripts/deployEnclave.ts +++ b/packages/enclave-contracts/scripts/deployEnclave.ts @@ -409,7 +409,7 @@ export const deployEnclave = async ( const currentVerifier = await ciphernodeRegistry.dkgFoldAttestationVerifier(); if (currentVerifier !== dkgFoldAttestationVerifierAddress) { - const tx = await ciphernodeRegistry.setDkgFoldAttestationVerifier( + const tx = await ciphernodeRegistry.setInitialDkgFoldAttestationVerifier( dkgFoldAttestationVerifierAddress, ); await tx.wait(); diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 3b11f17c7..7910a551f 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -69,10 +69,6 @@ describe("Enclave", function () { const data = "0xda7a"; const proof = "0x1337"; - const DKG_FOLD_ATTESTATION_TYPEHASH = ethers.id( - "DkgFoldAttestation(uint256 chainId,uint256 e3Id,uint256 partyId,bytes32 skAggCommit,bytes32 esmAggCommit)", - ); - /** Public inputs layout for `DkgFoldAttestationVerifier` with `h` honest parties. */ const encodeMockDkgProofForAttestation = ( pkCommitment: string, @@ -103,25 +99,28 @@ describe("Enclave", function () { const signFoldAttestation = async ( signer: Signer, chainId: bigint, + verifyingContract: string, e3Id: number, partyId: number, skAggCommit: string, esmAggCommit: string, ): Promise => { - const digest = ethers.keccak256( - ethers.AbiCoder.defaultAbiCoder().encode( - ["bytes32", "uint256", "uint256", "uint256", "bytes32", "bytes32"], - [ - DKG_FOLD_ATTESTATION_TYPEHASH, - chainId, - e3Id, - partyId, - skAggCommit, - esmAggCommit, - ], - ), - ); - return signer.signMessage(ethers.getBytes(digest)); + const domain = { + name: "EnclaveDkgFoldAttestation", + version: "1", + chainId, + verifyingContract, + }; + const types = { + DkgFoldAttestation: [ + { name: "e3Id", type: "uint256" }, + { name: "partyId", type: "uint256" }, + { name: "skAggCommit", type: "bytes32" }, + { name: "esmAggCommit", type: "bytes32" }, + ], + }; + const value = { e3Id, partyId, skAggCommit, esmAggCommit }; + return signer.signTypedData(domain, types, value); }; /** Proof + attestation bundle for `publishCommittee` when proof aggregation is enabled. */ @@ -129,6 +128,7 @@ describe("Enclave", function () { operators: Signer[], e3Id: number, publicKey: string, + verifyingContract: string, ): Promise<{ proof: string; bundle: string }> => { const pkCommitment = ethers.keccak256(publicKey); const h = operators.length; @@ -175,6 +175,7 @@ describe("Enclave", function () { signature: await signFoldAttestation( operator, chainId, + verifyingContract, e3Id, partyId, skCommits[i]!, @@ -388,7 +389,7 @@ describe("Enclave", function () { const dkgFoldAttestationVerifier = await ethers.deployContract( "DkgFoldAttestationVerifier", ); - await ciphernodeRegistryContract.setDkgFoldAttestationVerifier( + await ciphernodeRegistryContract.setInitialDkgFoldAttestationVerifier( await dkgFoldAttestationVerifier.getAddress(), ); @@ -450,6 +451,7 @@ describe("Enclave", function () { ticketToken, usdcToken, slashingManager, + dkgFoldAttestationVerifier, request, mocks: { decryptionVerifier, e3Program, mockComputeProvider }, }; @@ -1181,6 +1183,7 @@ describe("Enclave", function () { operator1, operator2, operator3, + dkgFoldAttestationVerifier, } = await loadFixture(setup); const e3Id = 0; @@ -1195,6 +1198,7 @@ describe("Enclave", function () { operators, e3Id, data, + await dkgFoldAttestationVerifier.getAddress(), ); await setupAndPublishCommittee( ciphernodeRegistryContract, diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index 7c70fe02a..70c796c27 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -187,7 +187,7 @@ describe("CiphernodeRegistryOwnable", function () { const dkgFoldAttestationVerifier = await ethers.deployContract( "DkgFoldAttestationVerifier", ); - await registry.setDkgFoldAttestationVerifier( + await registry.setInitialDkgFoldAttestationVerifier( await dkgFoldAttestationVerifier.getAddress(), ); diff --git a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts index 54c840303..9880f7c10 100644 --- a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts +++ b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts @@ -447,7 +447,9 @@ describe("SlashingManager", function () { .withArgs(REASON_PT_0, Object.values(policy)); }); - it("should revert if proof required but appeal window set", async function () { + it("should allow proof-based policy with appeal window (deferred execution)", async function () { + // Lane A policies may now opt into a non-zero appealWindow, deferring + // _executeSlash to give the accused a window to file an appeal. const { slashingManager, _mockVerifier } = await loadFixture(setup); const policy = { @@ -464,7 +466,7 @@ describe("SlashingManager", function () { await expect( slashingManager.setSlashPolicy(REASON_PT_0, policy), - ).to.be.revertedWithCustomError(slashingManager, "InvalidPolicy"); + ).to.not.be.revert(ethers); }); it("should revert if no proof required but no appeal window", async function () { @@ -1267,10 +1269,12 @@ describe("SlashingManager", function () { .connect(proposer) .proposeSlash(0, operatorAddress, proof); - // Cannot appeal proof-verified slashes — appeal window is 0 so it's already expired + // Cannot appeal proof-verified slashes whose policy has appealWindow == 0: + // they auto-execute inside `proposeSlash`, so the proposal is already + // marked executed by the time the accused tries to file an appeal. await expect( slashingManager.connect(operator).fileAppeal(0, "Cannot appeal proof"), - ).to.be.revertedWithCustomError(slashingManager, "AppealWindowExpired"); + ).to.be.revertedWithCustomError(slashingManager, "AlreadyExecuted"); }); it("should allow governance to resolve appeal (approve)", async function () { diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index e93ecf649..81bd43da3 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -21,6 +21,9 @@ chains: fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" deploy_block: 7 + dkg_fold_attestation_verifier: + address: "0x9d4454B023096f34B160D6B654540c56A1F81688" + deploy_block: 32 program: dev: true From 5bd6ca11a84e35122ea8f71c4945e244da5cb9b9 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 11:41:43 +0200 Subject: [PATCH 29/54] fix coderabbit nits --- crates/tests/tests/integration.rs | 19 +++-- crates/zk-prover/scripts/build_fixtures.sh | 22 +++++- .../src/circuits/aggregation/node_dkg_fold.rs | 11 --- crates/zk-prover/tests/fixtures/dummy.json | 2 +- docs/pages/cryptography.mdx | 22 +++--- docs/pages/internals/dkg.mdx | 21 +++--- examples/CRISP/client/.env.example | 2 +- examples/CRISP/test/crisp.spec.ts | 21 ++++-- .../contracts/Enclave.sol/Enclave.json | 2 +- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 50 ++++++------- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../ISlashingManager.json | 2 +- .../CiphernodeRegistryOwnable.json | 74 +++++++++---------- .../interfaces/ICiphernodeRegistry.sol | 4 +- .../registry/CiphernodeRegistryOwnable.sol | 26 ++++++- .../contracts/test/MockCiphernodeRegistry.sol | 4 +- .../verifiers/DkgFoldAttestationVerifier.sol | 18 +++-- .../dkgFoldAttestationVerifier.ts | 10 ++- .../enclave-contracts/tasks/ciphernode.ts | 57 ++++++++++++-- templates/default/enclave.config.yaml | 2 +- templates/default/scripts/anvil-automine.mjs | 14 +++- tests/integration/base.sh | 4 +- tests/integration/persist.sh | 2 +- 24 files changed, 250 insertions(+), 143 deletions(-) diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index e03bc4830..23c4ff5b1 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -210,15 +210,24 @@ async fn setup_test_zk_backend( let preset_out = circuits_dir.join(preset_subdir); let dist_marker = dist_preset.join("recursive/dkg/pk/pk.json"); let circuits_bin_marker = repo_root.join("circuits/bin/dkg/target/pk.json"); + // `circuits/bin` is preset-agnostic on disk — only `dist/circuits//.build-stamp.json` + // (written by `pnpm build:circuits --preset `) records which preset the + // most recent local build targeted. Without it we cannot tell whether `circuits/bin` + // matches `preset_subdir`, and copying the wrong preset's artifacts would silently + // produce invalid proofs. + let preset_build_stamp = dist_preset.join(".build-stamp.json"); if dist_marker.exists() { copy_dir_recursive(&dist_preset, &preset_out).await?; - } else if !circuits_bin_marker.exists() { - // CI `rust_integration_tests` does not run `pnpm build:circuits`; download the - // pinned release tarball when no local fixtures are present. + } else if !circuits_bin_marker.exists() || !preset_build_stamp.exists() { + // Either no local build exists, or the local build cannot be proven to match + // the requested preset; download the pinned release tarball instead. println!( - "No local circuit fixtures under dist/circuits or circuits/bin; \ - downloading release circuits via ensure_installed()..." + "No verifiable local circuit fixtures for preset `{}` \ + (need either dist/circuits/{}/recursive/dkg/pk/pk.json \ + or circuits/bin + dist/circuits/{}/.build-stamp.json); \ + downloading release circuits via ensure_installed()...", + preset_subdir, preset_subdir, preset_subdir ); let backend = ZkBackend::new(BBPath::Default(bb_binary), circuits_dir, work_dir); backend diff --git a/crates/zk-prover/scripts/build_fixtures.sh b/crates/zk-prover/scripts/build_fixtures.sh index 466eca693..651f424e8 100644 --- a/crates/zk-prover/scripts/build_fixtures.sh +++ b/crates/zk-prover/scripts/build_fixtures.sh @@ -26,7 +26,27 @@ pnpm install && pnpm build:circuits # Keep integration-test fixture in sync when the dummy circuit is built. dummy_artifact="./circuits/bin/dummy/dummy.json" fixture="./crates/zk-prover/tests/fixtures/dummy.json" +normalize_compiled_circuit_paths() { + # Noir emits machine-local absolute paths in file_map; keep fixtures stable. + jq ' + if .file_map then + .file_map |= with_entries( + .value |= if (.path | type) == "string" then + .path |= ( + if test("^/") and test("circuits/") then + sub("^.*?circuits/"; "circuits/") + else . + end + ) + else . + end + ) + else . + end + ' +} + if [ -f "$dummy_artifact" ]; then mkdir -p "$(dirname "$fixture")" - cp "$dummy_artifact" "$fixture" + normalize_compiled_circuit_paths <"$dummy_artifact" >"$fixture" fi diff --git a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs index 8c4854f06..87ef773a2 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -389,12 +389,6 @@ pub fn prove_dkg_aggregation( .map(address_to_field_hex) .collect(); - let committee_members: Vec = input - .committee_addresses - .iter() - .map(address_to_field_hex) - .collect(); - let witness = DkgAggregatorWitness { nodes_fold_vk: nodes_fold_vk.verification_key.clone(), nodes_fold_proof: proof_field_strings(&nodes_fold_proof)?, @@ -493,11 +487,6 @@ pub fn prove_decryption_aggregation_jobs( .map(address_to_field_hex) .collect(); - let committee_members: Vec = committee_addresses - .iter() - .map(address_to_field_hex) - .collect(); - let mut out = Vec::with_capacity(jobs.len()); for (i, job) in jobs.iter().enumerate() { let c6_fold = generate_sequential_c6_fold( diff --git a/crates/zk-prover/tests/fixtures/dummy.json b/crates/zk-prover/tests/fixtures/dummy.json index 051501b7d..637f61556 100644 --- a/crates/zk-prover/tests/fixtures/dummy.json +++ b/crates/zk-prover/tests/fixtures/dummy.json @@ -15,7 +15,7 @@ "file_map": { "50": { "source": "pub fn main(\n x: Field,\n y: Field,\n _sum: pub Field\n) {\n let sum = x + y;\n assert(sum == _sum);\n}\n", - "path": "/Users/ctrlc03/Documents/zk/enclave/circuits/bin/dummy/src/main.nr" + "path": "circuits/bin/dummy/src/main.nr" } }, "expression_width": { "Bounded": { "width": 4 } } diff --git a/docs/pages/cryptography.mdx b/docs/pages/cryptography.mdx index 6d4fb5ce7..d4214eb78 100644 --- a/docs/pages/cryptography.mdx +++ b/docs/pages/cryptography.mdx @@ -231,19 +231,21 @@ At `publishCommittee` the registry has already finalised `topNodes` (the address party 0 = lowest address, party 1 = next, ...), so the on-chain canonical mapping from a `partyId` to an operator address is `topNodes[partyId]`. -Each node's fold attestation signs an EIP-712-style digest over -`(chainId, e3Id, partyId, skAggCommit, esmAggCommit)` with the operator's registered key. The -on-chain +Each node's fold attestation signs the canonical **EIP-712 typed-data digest** +(`keccak256("\x19\x01" || domainSeparator || structHash)`). The **domain separator** binds `chainId` +and `verifyingContract` (the deployed `DkgFoldAttestationVerifier` address); off-chain signers must +include those domain fields. The **struct hash** binds `(e3Id, partyId, skAggCommit, esmAggCommit)` +— the tuple values in the signed payload, separate from the domain. The on-chain [`DkgFoldAttestationVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol) then enforces four properties per attestation: -1. **Slot binding**: `getCommitteeNodeAt(e3Id, binding.partyId) == binding.node`. The aggregator - cannot claim that the operator at `topNodes[0]` is party 1 — the registry's canonical slot - mapping is checked directly. Available before `publishCommittee` completes (the registry sorts - `topNodes` at finalisation, well before the attestation verifier runs). -2. **Signer = registered operator**: `ecrecover` over the signed digest must equal that `node` - address, and the address must be an active committee member for this E3 - (`isCommitteeMemberActive(e3Id, node)`). +1. **Slot binding**: `canonicalCommitteeNodeAt(e3Id, binding.partyId) == binding.node`. The + aggregator cannot claim that the operator at `topNodes[0]` is party 1 — the registry's canonical + slot mapping is checked directly. Available before `publishCommittee` completes (the registry + sorts `topNodes` at finalisation, well before the attestation verifier runs). +2. **Signer = registered operator**: `ecrecover` on the EIP-712 digest (with this verifier as + `verifyingContract`) must equal `binding.node`, and the address must be an active committee + member for this E3 (`isCommitteeMemberActive(e3Id, node)`). 3. **Commitments match the DKG proof**: the signed `skAggCommit` and `esmAggCommit` must equal the `partyId`'s slot in the DKG aggregator proof's public inputs. 4. **Honest-set cardinality**: the bundle must contain exactly one attestation per honest party in diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index ec3a3ca89..5bd43d2c0 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -331,7 +331,7 @@ aggregator. ## Proof Aggregation and On-Chain Anchoring E3 programs can opt into **proof aggregation** by setting `proofAggregationEnabled = true` at -request time. In that mode the network does not post per-node, per-circuit proofs on chain. Instead +request time. In that mode the network does not post per-node, per-circuit proofs on-chain. Instead the aggregator submits two recursive proofs — one for DKG, one for decryption — together with per-node attestations that bind each surfaced commitment to a specific registered operator. @@ -386,14 +386,15 @@ the registry runs two checks in order: 2. **Fold-attestation verification** via [`DkgFoldAttestationVerifier`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol). It walks the bundle's `(partyId, node)` bindings — strictly ascending by `partyId`, no duplicates - — and for each binding: requires `getCommitteeNodeAt(e3Id, partyId) == node` (slot binding), - requires `isCommitteeMemberActive(e3Id, node)`, recovers the signer of the attestation via - `ecrecover` against the EIP-191/712 digest, requires `signer == node`, and checks that the signed - `skAggCommit` / `esmAggCommit` match the DKG proof's surfaced commitments at that party's slot. - The honest-set cardinality must equal the honest-party count derived from the proof's - public-input shape (`(len - 6) / 3`). The slot binding prevents an aggregator from reassigning a - node's signed attestation to a different slot — even an actively cooperating signer cannot be - "moved" between partyIds because the registry's own slot mapping is checked structurally. + — and for each binding: requires `canonicalCommitteeNodeAt(e3Id, partyId) == node` (slot + binding), requires `isCommitteeMemberActive(e3Id, node)`, recovers the signer via `ecrecover` on + the canonical EIP-712 typed-data digest (with `chainId` and this verifier address in the domain + separator), requires `signer == node`, and checks that the signed `skAggCommit` / `esmAggCommit` + match the DKG proof's surfaced commitments at that party's slot. The honest-set cardinality must + equal the honest-party count derived from the proof's public-input shape (`(len - 6) / 3`). The + slot binding prevents an aggregator from reassigning a node's signed attestation to a different + slot — even an actively cooperating signer cannot be "moved" between partyIds because the + registry's own slot mapping is checked structurally. Only if both succeed does the registry store the per-party anchors: @@ -430,7 +431,7 @@ decodes as `(proofType, voters[], agrees[], dataHashes[], signatures[], evidence - All `dataHashes[i]` equal a common `dataHash` (voters must agree on the same evidence, not just "something bad"). - `keccak256(evidence) == commonDataHash`. The `evidence` field is the raw - `abi.encode(proof.data, public_signals)` of the faulty proof, transported on chain so the contract + `abi.encode(proof.data, public_signals)` of the faulty proof, transported on-chain so the contract — and any third party — can recompute the dataHash from the bytes the committee voted on. On the Rust side, the evidence bytes are populated for every accusation path that leads to a slash diff --git a/examples/CRISP/client/.env.example b/examples/CRISP/client/.env.example index 60e2dac4a..d69bce5d2 100644 --- a/examples/CRISP/client/.env.example +++ b/examples/CRISP/client/.env.example @@ -2,6 +2,6 @@ VITE_ENCLAVE_API=http://127.0.0.1:4000 VITE_WALLETCONNECT_PROJECT_ID= # Voting eligibility token (MockVotingToken). Set after CRISP deploy — see deploy output / server .env. # Enclave fee token (MockUSDC) is separate: packages/enclave-contracts/deployed_contracts.json → MockUSDC. -VITE_CRISP_TOKEN=0x9d4454B023096f34B160D6B654540c56A1F81688 +VITE_CRISP_TOKEN= # Addresses of requesters for which to show rounds in the UI. Comma separated Ethereum addresses. VITE_E3_REQUESTERS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266,0x70997970C51812dc3A010C7d01b50e0d17dc79C8 diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index a52c685ce..ae15c4cb1 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -8,22 +8,29 @@ import { ConsoleMessage, Page } from '@playwright/test' import { testWithSynpress } from '@synthetixio/synpress' import { MetaMask, metaMaskFixtures } from '@synthetixio/synpress/playwright' import basicSetup from './wallet-setup/basic.setup' -import { execSync } from 'child_process' +import { execFileSync } from 'child_process' import { config } from 'dotenv' import path from 'path' +const CLI = path.join(process.cwd(), 'target', 'debug', 'cli') + config({ path: path.join(process.cwd(), 'server', '.env') }) +config({ path: path.join(process.cwd(), 'client', '.env') }) const E3_DURATION = parseInt(process.env.E3_DURATION as string, 10) * 1000 const OUTPUT_DECRYPTION_WAIT = 80_000 // A small buffer for decryption +function crispTokenAddress(): string { + const tokenAddress = process.env.VITE_CRISP_TOKEN + if (!tokenAddress) { + throw new Error('VITE_CRISP_TOKEN must be set (see client/.env after deploy)') + } + return tokenAddress +} + async function runCliInit(): Promise { try { - // Execute the command and wait for it to complete - const tokenAddress = process.env.VITE_CRISP_TOKEN ?? '0x9d4454B023096f34B160D6B654540c56A1F81688' - const output = execSync(`pnpm cli init --token-address ${tokenAddress} --balance-threshold 1000`, { - encoding: 'utf-8', - }) + const output = execFileSync(CLI, ['init', '--token-address', crispTokenAddress(), '--balance-threshold', '1000'], { encoding: 'utf-8' }) console.log('Command output:', output) const lines = output.trim().split('\n') const lastLine = lines[lines.length - 1].trim() @@ -40,7 +47,7 @@ async function runCliInit(): Promise { async function checkE3Ready(e3id: number): Promise { try { - const output = execSync(`pnpm cli check-e3-ready --e3id ${e3id}`, { + const output = execFileSync(CLI, ['check-e3-ready', '--e3id', String(e3id)], { encoding: 'utf-8', }) const lines = output.trim().split('\n') diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index d204daefb..100bd25c3 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -2564,5 +2564,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" + "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 7dda1e846..eb904ab08 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -1005,5 +1005,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" + "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 1b4330557..020a32136 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -631,6 +631,30 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + } + ], + "name": "canonicalCommitteeNodeAt", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -778,30 +802,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "e3Id", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "partyId", - "type": "uint256" - } - ], - "name": "getCommitteeNodeAt", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -1186,5 +1186,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" + "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index da9b477ee..311215b95 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2097,5 +2097,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" + "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index 9deb40fc9..ff8483662 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -1006,5 +1006,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" + "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index 044982d45..c299cc656 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -772,6 +772,30 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + } + ], + "name": "canonicalCommitteeNodeAt", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1014,30 +1038,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "e3Id", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "partyId", - "type": "uint256" - } - ], - "name": "getCommitteeNodeAt", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -1596,30 +1596,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6148dd806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102b8575f3560e01c80639a7a2ffc11610177578063da881e5a116100d5578063ebf0c7171161008f578063ebf0c71714610694578063f16505361461069c578063f2fde38b146106b6578063f379b0df146106c9578063f52fd80314610703578063f6fc05d514610773578063fa9325691461077c575f5ffd5b8063da881e5a14610623578063dbb06c9314610636578063e4be6e3d14610648578063e59e46951461065b578063e6745e131461066e578063e82f3b7014610681575f5ffd5b8063b8ab470411610131578063b8ab470414610584578063bff232c1146105a6578063c2b40ae4146105b9578063c3a0ec30146105d8578063c8fe182d146105e9578063ca2869a0146105f1578063cd6dc68714610610575f5ffd5b80639a7a2ffc146104f05780639f0f874a1461052c578063a016493014610535578063a8a4d69b14610555578063acc5249414610568578063b2d5d1ac1461057b575f5ffd5b806350e6fad31161022457806385814243116101de57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ac5780638da5cb5b146104c25780638e5ce3ad146104ca5780639015d371146104dd575f5ffd5b806350e6fad3146103e25780635d504776146103ec57806362cc89a8146103ff57806370e36bbe1461041f578063715018a6146104325780637c92f5241461043a575f5ffd5b80632800d829116102755780632800d82914610351578063291a691b146103645780632e7b716d14610387578063323beaa51461039a5780634d6861a6146103ad57806350e6d94c146103c0575f5ffd5b8063096b810a146102bc578063099a161a146102d15780630b45f8e9146102f75780630bbfade71461030a5780630f3e34121461031d57806317d6112014610330575b5f5ffd5b6102cf6102ca366004613c75565b61078f565b005b6102e46102df366004613c90565b6108db565b6040519081526020015b60405180910390f35b6102cf610305366004613c75565b610914565b6102cf610318366004613ceb565b6109b5565b6102cf61032b366004613c90565b610bf9565b61034361033e366004613c90565b610c3c565b6040516102ee929190613e0c565b6102e461035f366004613c90565b610de5565b610377610372366004613e39565b610e31565b60405190151581526020016102ee565b610377610395366004613c75565b61100b565b6102cf6103a8366004613c75565b6110be565b6103776103bb366004613c90565b6111cf565b6103776103ce366004613c75565b60066020525f908152604090205460ff1681565b6102e46202a30081565b6103776103fa366004613e72565b61120e565b600d54610412906001600160a01b031681565b6040516102ee9190613ea0565b6102cf61042d366004613c75565b611251565b6102cf6112c7565b61044d610448366004613eb4565b6112da565b6040805192835263ffffffff9091166020830152016102ee565b600154610412906001600160a01b031681565b6102cf610488366004613c75565b611481565b6102e461049b366004613c90565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102e4565b6104126115bf565b600b54610412906001600160a01b031681565b6103776104eb366004613c75565b6115ed565b6105166104fe366004613c75565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ee565b6102e460035481565b610548610543366004613c90565b61160a565b6040516102ee9190613ee9565b610377610563366004613e72565b6116a0565b6102cf610576366004613c75565b6116e3565b6102e4600e5481565b610597610592366004613c90565b61177a565b6040516102ee93929190613efb565b6102cf6105b4366004613c75565b6118c5565b6102e46105c7366004613c90565b60086020525f908152604090205481565b6001546001600160a01b0316610412565b6102cf61193d565b6102e46105ff366004613c90565b5f9081526008602052604090205490565b6102cf61061e366004613f3d565b6119b9565b610377610631366004613c90565b611b16565b5f54610412906001600160a01b031681565b600c54610412906001600160a01b031681565b6102cf610669366004613c75565b611df9565b6102cf61067c366004613f67565b611e71565b6102e461068f366004613c90565b612034565b6102e4612065565b6106a4601481565b60405160ff90911681526020016102ee565b6102cf6106c4366004613c75565b612077565b6004546106e59064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ee565b610744610711366004613c90565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ee949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e460025481565b61041261078a366004613f67565b6120b1565b6107976115bf565b6001600160a01b0316336001600160a01b031614806107c057506001546001600160a01b031633145b6107dd57604051632864c4e160e01b815260040160405180910390fd5b6107e6816115ed565b819061080f576040516381e5828960e01b81526004016108069190613ea0565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff169061083d906004908361211e565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161086a83613f9b565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a60205260408120600481015461090a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61091c6123c0565b600c546001600160a01b0316156109455760405162035f5560e61b815260040160405180910390fd5b6001600160a01b03811661096c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383169081179091556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b905f90a250565b5f888152600a602052604090206002815460ff1660038111156109da576109da613fb0565b146109f857604051634f4b461f60e11b815260040160405180910390fd5b600481015415610a1b5760405163632a22bb60e01b815260040160405180910390fd5b85610a3957604051636caad1ed60e11b815260040160405180910390fd5b5f610a9d82600601805480602002602001604051908101604052809291908181526020018280548015610a9357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610a75575b50505050506123f2565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610aef573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b169190810190614140565b9050806101c0015115610b3357610b338b828a858b8b8b8b6124f8565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610b91575f5ffd5b505af1158015610ba3573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610be496959493929190614302565b60405180910390a25050505050505050505050565b610c016123c0565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610c7257610c72613fc4565b604051908082528060200260200182016040528015610c9b578160200160208202803683370190505b509450806001600160401b03811115610cb657610cb6613fc4565b604051908082528060200260200182016040528015610cdf578160200160208202803683370190505b5093505f805b83811015610ddb575f856006018281548110610d0357610d0361434f565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610d4957610d49613fb0565b03610dd25780888481518110610d6157610d6161434f565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610db957610db961434f565b602090810291909101015282610dce81614363565b9350505b50600101610ce5565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610e0957610e09613fb0565b03610e2757604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610e5c5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610e8057610e80613fb0565b14610e9e576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610ee5573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f09919061437b565b905080610f1c60408601602087016143a5565b63ffffffff161115610f3460408601602087016143a5565b829091610f62576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610806565b5050815460ff1916600190811783558201859055436002830155600354610f8990426143be565b6003830155610f9d60058301856002613b73565b50610fa6612065565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ff7928a928a92916143d1565b60405180910390a250600195945050505050565b5f611015826115ed565b61102057505f919050565b6001546001600160a01b0316611049576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790611079908590600401613ea0565b602060405180830381865afa158015611094573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b89190614421565b92915050565b6110c66123c0565b600d546001600160a01b0316806110f05760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461113157604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610806565b50505f6202a300600e5461114591906143be565b9050804281811015611173576040516337c8270b60e01b815260048101929092526024820152604401610806565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690555f600e8190556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b9190a2505050565b5f818152600a602052604081206001815460ff1660038111156111f4576111f4613fb0565b1461120157505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561124957611249613fb0565b149392505050565b6112596123c0565b6001600160a01b0381166112805760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6112cf6123c0565b6112d85f6126f4565b565b600b545f9081906001600160a01b031633146113095760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561132e5761132e613fb0565b1461134c57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561138c5761138c613fb0565b1461139c57600b01549150611479565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916113d083613f9b565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611421929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6114896115bf565b6001600160a01b0316336001600160a01b031614806114b257506001546001600160a01b031633145b6114cf57604051632864c4e160e01b815260040160405180910390fd5b6114d8816115ed565b6115bc5760048054600160281b900464ffffffffff1690611502906001600160a01b038416612764565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161155383614363565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016108cf565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a6020526040902060048101546060919061163d576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561169357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611675575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156116da576116da613fb0565b14159392505050565b6116eb6123c0565b6001600160a01b0381166117125760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611766906202a300906143be565b60405190815260200160405180910390a250565b5f81815260096020526040902054606090819081906117ac576040516322e679e360e11b815260040160405180910390fd5b5f848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561181157602002820191905f5260205f20905b8154815260200190600101908083116117fd575b505050505092508180548060200260200160405190810160405280929190818152602001828054801561186157602002820191905f5260205f20905b81548152602001906001019080831161184d575b50505050509150808054806020026020016040519081016040528092919081815260200182805480156118b157602002820191905f5260205f20905b81548152602001906001019080831161189d575b505050505090509250925092509193909250565b6118cd6123c0565b6001600160a01b0381166118f45760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6119456123c0565b600d546001600160a01b03168061196f5760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690555f600e8190556040516001600160a01b038316917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a250565b5f6119c261293a565b805490915060ff600160401b82041615906001600160401b03165f811580156119e85750825b90505f826001600160401b03166001148015611a035750303b155b905081158015611a11575080155b15611a2f5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611a5957845460ff60401b1916600160401b1785555b6001600160a01b038716611a805760405163d92e233d60e01b815260040160405180910390fd5b611a8933612962565b611a9560046014612973565b611a9e86610bf9565b611aa66115bf565b6001600160a01b0316876001600160a01b031614611ac757611ac787612077565b8315611b0d57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff166003811115611b3a57611b3a613fb0565b03611b5857604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611b7057611b70613fb0565b14611b8e57604051631860f69960e31b815260040160405180910390fd5b80600301544211611bb257604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611c97578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611c78575f5ffd5b505af1158015611c8a573d5f5f3e3d5ffd5b505f979650505050505050565b611ca0826129f2565b815460ff191660021782556006820154600b83018190555f816001600160401b03811115611cd057611cd0613fc4565b604051908082528060200260200182016040528015611cf9578160200160208202803683370190505b5090505f5b82811015611d6b57846009015f866006018381548110611d2057611d2061434f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611d5857611d5861434f565b6020908102919091010152600101611cfe565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611dae575f5ffd5b505af1158015611dc0573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ff792919061443a565b611e016123c0565b6001600160a01b038116611e285760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611e9557611e95613fb0565b03611eb357604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611ecb57611ecb613fb0565b14611ee957604051631860f69960e31b815260040160405180910390fd5b8060030154421115611f0e57604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611f405760405163257309f160e11b815260040160405180910390fd5b611f493361100b565b611f665760405163149fbcfd60e11b815260040160405180910390fd5b611f71338385612b18565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611ff090839083612ce9565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480612060576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61207260046014612eea565b905090565b61207f6123c0565b6001600160a01b0381166120a8575f604051631e4fbdf760e01b81526004016108069190613ea0565b6115bc816126f4565b5f828152600a60205260408120600601805483908082106120ee576040516326c5c55b60e11b815260048101929092526024820152604401610806565b50508083815481106121025761210261434f565b5f918252602090912001546001600160a01b0316949350505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061215d5760405162461bcd60e51b81526004016108069061444c565b825464ffffffffff600160281b909104811690821681116121bb5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610806565b825f5b81866001015f6121ce8488612fe3565b64ffffffffff1681526020019081526020015f20819055505f8160016121f49190614496565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff16811161222957506123b8565b600185165f036122f0575f612248836122438860016144af565b612fe3565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916122a9916004016144cc565b602060405180830381865af41580156122c4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e8919061437b565b9350506123a4565b5f612300836122436001896144fc565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612361916004016144cc565b602060405180830381865af415801561237c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123a0919061437b565b9350505b50647fffffffff600194851c1693016121be565b505050505050565b336123c96115bf565b6001600160a01b0316146112d8573360405163118cdaa760e01b81526004016108069190613ea0565b80515f9081612402826014614519565b6001600160401b0381111561241957612419613fc4565b6040519080825280601f01601f191660200182016040528015612443576020820181803683370190505b5090505f5b828110156124e8575f8582815181106124635761246361434f565b602002602001015160601b90505f82601461247e9190614519565b90505f5b60148110156124da5782816014811061249d5761249d61434f565b1a60f81b856124ac83856143be565b815181106124bc576124bc61434f565b60200101906001600160f81b03191690815f1a905350600101612482565b505050806001019050612448565b5080516020909101209392505050565b8261251657604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b815260040161254d9493929190614530565b602060405180830381865afa158015612568573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061258c9190614421565b6125a95760405163051d8aa760e51b815260040160405180910390fd5b806125c757604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b03166125f057604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b815260040161263f979695949392919061454f565b5f60405180830381865afa158015612659573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526126809190810190614629565b5f8e8152600f6020908152604090912084519497509295509093506126a89290860190613c14565b505f8b815260106020908152604090912083516126c792850190613c14565b505f8b815260116020908152604090912082516126e692840190613c14565b505050505050505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106127b35760405162461bcd60e51b81526004016108069061444c565b825464ffffffffff908116908216106128065760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610806565b6128118160016144af565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6128488487612fe3565b64ffffffffff16815260208101919091526040015f20556001831615612933575f612878826122436001876144fc565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916128d9916004016144cc565b602060405180830381865af41580156128f4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612918919061437b565b647fffffffff600195861c1694909350919091019050612838565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006110b8565b61296a613000565b6115bc81613025565b602060ff821611156129c15760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610806565b6129d2600160ff831681901b61470d565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612b13575f612a0d8260016143be565b90505b82811015612b0a575f846006018381548110612a2e57612a2e61434f565b5f9182526020822001546006870180546001600160a01b0390921693509084908110612a5c57612a5c61434f565b5f918252602090912001546001600160a01b0390811691508216811015612b005780866006018581548110612a9357612a9361434f565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612ad457612ad461434f565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612a10565b506001016129f9565b505050565b5f8211612b385760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612b61576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612b979161470d565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612bde573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c02919061437b565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c55573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c79919061437b565b90505f8111612c9b5760405163aeaddff160e01b815260040160405180910390fd5b5f612ca68284614720565b90505f8111612cc85760405163149fbcfd60e11b815260040160405180910390fd5b80861115611b0d5760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612d6757508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612ee3565b5f5f90505f876009015f855f81548110612d8357612d8361434f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612e0b575f896009015f878481548110612dcd57612dcd61434f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e02578092508193505b50600101612dac565b50808610612e1f575f945050505050612ee3565b5f88600a015f868581548110612e3757612e3761434f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612e7457612e74613fb0565b021790555086848381548110612e8c57612e8c61434f565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612f3d5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610806565b602060ff83161115612f615760405162461bcd60e51b81526004016108069061473f565b8254600160281b900464ffffffffff1680612f8060ff85166002614890565b64ffffffffff161015612fd05760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610806565b612fdb84828561302d565b949350505050565b5f81612ff660ff851663ffffffff6148a9565b612ee391906144af565b6130086130f5565b6112d857604051631afcd79f60e31b815260040160405180910390fd5b61207f613000565b5f602060ff831611156130525760405162461bcd60e51b81526004016108069061473f565b8264ffffffffff165f03613070576130698261310e565b9050612ee3565b5f61307c836001614496565b60ff166001600160401b0381111561309657613096613fc4565b6040519080825280602002602001820160405280156130bf578160200160208202803683370190505b5090506130ce858585846137a8565b808360ff16815181106130e3576130e361434f565b60200260200101519150509392505050565b5f6130fe61293a565b54600160401b900460ff16919050565b5f8160ff165f0361312057505f919050565b8160ff1660010361315257507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361318457507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036131b657507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036131e857507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361321a57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361324c57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361327e57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036132b057507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036132e257507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361331457507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361334657507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361337857507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036133aa57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036133dc57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361340e57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361344057507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361347257507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff166012036134a457507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036134d657507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361350857507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361353a57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361356c57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361359e57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036135d057507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361360257507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361363457507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361366657507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361369857507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036136ca57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036136fc57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361372e57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361376057507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610806565b602060ff831611156137cc5760405162461bcd60e51b81526004016108069061473f565b5f8364ffffffffff16116138305760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610806565b5f61383c6001856144fc565b9050600181165f0361388f57846001015f6138575f84612fe3565b64ffffffffff1681526020019081526020015f2054825f8151811061387e5761387e61434f565b6020026020010181815250506138b7565b6138985f61310e565b825f815181106138aa576138aa61434f565b6020026020010181815250505b5f5b8360ff168160ff1610156123b857600182165f036139af5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061390b5761390b61434f565b602002602001015181526020016139218561310e565b8152506040518263ffffffff1660e01b815260040161394091906144cc565b602060405180830381865af415801561395b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061397f919061437b565b8361398b836001614496565b60ff168151811061399e5761399e61434f565b602002602001018181525050613b60565b5f6139bb826001614496565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613a5d575f876001015f613a12856001613a019190614496565b60018864ffffffffff16901c612fe3565b64ffffffffff1681526020019081526020015f205490508085846001613a389190614496565b60ff1681518110613a4b57613a4b61434f565b60200260200101818152505050613b5e565b5f876001015f613a748560018861224391906144fc565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613acb57613acb61434f565b60200260200101518152506040518263ffffffff1660e01b8152600401613af291906144cc565b602060405180830381865af4158015613b0d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b31919061437b565b85613b3d856001614496565b60ff1681518110613b5057613b5061434f565b602002602001018181525050505b505b647fffffffff600192831c1691016138b9565b600183019183908215613c04579160200282015f5b83821115613bd257833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613b88565b8015613c025782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613bd2565b505b50613c10929150613c4d565b5090565b828054828255905f5260205f20908101928215613c04579160200282015b82811115613c04578251825591602001919060010190613c32565b5b80821115613c10575f8155600101613c4e565b6001600160a01b03811681146115bc575f5ffd5b5f60208284031215613c85575f5ffd5b8135612ee381613c61565b5f60208284031215613ca0575f5ffd5b5035919050565b5f5f83601f840112613cb7575f5ffd5b5081356001600160401b03811115613ccd575f5ffd5b602083019150836020828501011115613ce4575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613d02575f5ffd5b8835975060208901356001600160401b03811115613d1e575f5ffd5b613d2a8b828c01613ca7565b9098509650506040890135945060608901356001600160401b03811115613d4f575f5ffd5b613d5b8b828c01613ca7565b90955093505060808901356001600160401b03811115613d79575f5ffd5b613d858b828c01613ca7565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613dd25781516001600160a01b0316865260209586019590910190600101613dab565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613dd2578151865260209586019590910190600101613dee565b604081525f613e1e6040830185613d99565b8281036020840152613e308185613ddc565b95945050505050565b5f5f5f60808486031215613e4b575f5ffd5b833592506020840135915060808401851015613e65575f5ffd5b6040840190509250925092565b5f5f60408385031215613e83575f5ffd5b823591506020830135613e9581613c61565b809150509250929050565b6001600160a01b0391909116815260200190565b5f5f5f60608486031215613ec6575f5ffd5b833592506020840135613ed881613c61565b929592945050506040919091013590565b602081525f612ee36020830184613d99565b606081525f613f0d6060830186613ddc565b8281036020840152613f1f8186613ddc565b90508281036040840152613f338185613ddc565b9695505050505050565b5f5f60408385031215613f4e575f5ffd5b8235613f5981613c61565b946020939093013593505050565b5f5f60408385031215613f78575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613fa957613fa9613f87565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613ffb57613ffb613fc4565b60405290565b604051601f8201601f191681016001600160401b038111828210171561402957614029613fc4565b604052919050565b805160048110612060575f5ffd5b5f82601f83011261404e575f5ffd5b604080519081016001600160401b038111828210171561407057614070613fc4565b8060405250806040840185811115614086575f5ffd5b845b818110156140a0578051835260209283019201614088565b509195945050505050565b805161206081613c61565b805160ff81168114612060575f5ffd5b5f82601f8301126140d5575f5ffd5b81516001600160401b038111156140ee576140ee613fc4565b614101601f8201601f1916602001614001565b818152846020838601011115614115575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114612060575f5ffd5b5f60208284031215614150575f5ffd5b81516001600160401b03811115614165575f5ffd5b82016102008185031215614177575f5ffd5b61417f613fd8565b8151815261418f60208301614031565b6020820152604082810151908201526141ab856060840161403f565b606082015260a082015160808201526141c660c083016140ab565b60a08201526141d760e083016140b6565b60c08201526101008201516001600160401b038111156141f5575f5ffd5b614201868285016140c6565b60e08301525061421461012083016140ab565b61010082015261422761014083016140ab565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561425d575f5ffd5b614269868285016140c6565b6101808301525061427d6101c083016140ab565b6101a08201526142906101e08301614131565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613dd25781546001600160a01b03168652602090950194600191820191016142b3565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f614314608083018961429e565b828103602084015261432781888a6142da565b905085604084015282810360608401526143428185876142da565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161437457614374613f87565b5060010190565b5f6020828403121561438b575f5ffd5b5051919050565b803563ffffffff81168114612060575f5ffd5b5f602082840312156143b5575f5ffd5b612ee382614392565b808201808211156110b8576110b8613f87565b84815260a0810160208201855f5b600281101561440c5763ffffffff6143f683614392565b16835260209283019291909101906001016143df565b50505060608201939093526080015292915050565b5f60208284031215614431575f5ffd5b612ee382614131565b604081525f613e1e604083018561429e565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff81811683821601908111156110b8576110b8613f87565b64ffffffffff81811683821601908111156110b8576110b8613f87565b6040810181835f5b60028110156144f35781518352602092830192909101906001016144d4565b50505092915050565b64ffffffffff82811682821603908111156110b8576110b8613f87565b80820281158282048414176110b8576110b8613f87565b848152836020820152606060408201525f613f336060830184866142da565b60018060a01b038816815286602082015285604082015260a060608201525f61457c60a0830186886142da565b828103608084015261458f8185876142da565b9a9950505050505050505050565b5f6001600160401b038211156145b5576145b5613fc4565b5060051b60200190565b5f82601f8301126145ce575f5ffd5b81516145e16145dc8261459d565b614001565b8082825260208201915060208360051b860101925085831115614602575f5ffd5b602085015b8381101561461f578051835260209283019201614607565b5095945050505050565b5f5f5f6060848603121561463b575f5ffd5b83516001600160401b03811115614650575f5ffd5b8401601f81018613614660575f5ffd5b805161466e6145dc8261459d565b8082825260208201915060208360051b85010192508883111561468f575f5ffd5b6020840193505b828410156146b1578351825260209384019390910190614696565b8096505050505060208401516001600160401b038111156146d0575f5ffd5b6146dc868287016145bf565b92505060408401516001600160401b038111156146f7575f5ffd5b614703868287016145bf565b9150509250925092565b818103818111156110b8576110b8613f87565b5f8261473a57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115611479578085048111156147a1576147a1613f87565b60018416156147af57908102905b60019390931c928002614786565b5f826147cb575060016110b8565b816147d757505f6110b8565b81600181146147ed57600281146147f757614829565b60019150506110b8565b60ff84111561480857614808613f87565b6001841b915064ffffffffff82111561482357614823613f87565b506110b8565b5060208310610133831016604e8410600b8410161715614861575081810a64ffffffffff81111561485c5761485c613f87565b6110b8565b61487164ffffffffff8484614782565b8064ffffffffff0482111561488857614888613f87565b029392505050565b5f612ee364ffffffffff841664ffffffffff84166147bd565b64ffffffffff81811683821602908116908181146148c9576148c9613f87565b509291505056fea164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102b8575f3560e01c80639a7a2ffc11610177578063da881e5a116100d5578063ebf0c7171161008f578063ebf0c71714610694578063f16505361461069c578063f2fde38b146106b6578063f379b0df146106c9578063f52fd80314610703578063f6fc05d514610773578063fa9325691461077c575f5ffd5b8063da881e5a14610623578063dbb06c9314610636578063e4be6e3d14610648578063e59e46951461065b578063e6745e131461066e578063e82f3b7014610681575f5ffd5b8063b8ab470411610131578063b8ab470414610584578063bff232c1146105a6578063c2b40ae4146105b9578063c3a0ec30146105d8578063c8fe182d146105e9578063ca2869a0146105f1578063cd6dc68714610610575f5ffd5b80639a7a2ffc146104f05780639f0f874a1461052c578063a016493014610535578063a8a4d69b14610555578063acc5249414610568578063b2d5d1ac1461057b575f5ffd5b806350e6fad31161022457806385814243116101de57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ac5780638da5cb5b146104c25780638e5ce3ad146104ca5780639015d371146104dd575f5ffd5b806350e6fad3146103e25780635d504776146103ec57806362cc89a8146103ff57806370e36bbe1461041f578063715018a6146104325780637c92f5241461043a575f5ffd5b80632800d829116102755780632800d82914610351578063291a691b146103645780632e7b716d14610387578063323beaa51461039a5780634d6861a6146103ad57806350e6d94c146103c0575f5ffd5b8063096b810a146102bc578063099a161a146102d15780630b45f8e9146102f75780630bbfade71461030a5780630f3e34121461031d57806317d6112014610330575b5f5ffd5b6102cf6102ca366004613c75565b61078f565b005b6102e46102df366004613c90565b6108db565b6040519081526020015b60405180910390f35b6102cf610305366004613c75565b610914565b6102cf610318366004613ceb565b6109b5565b6102cf61032b366004613c90565b610bf9565b61034361033e366004613c90565b610c3c565b6040516102ee929190613e0c565b6102e461035f366004613c90565b610de5565b610377610372366004613e39565b610e31565b60405190151581526020016102ee565b610377610395366004613c75565b61100b565b6102cf6103a8366004613c75565b6110be565b6103776103bb366004613c90565b6111cf565b6103776103ce366004613c75565b60066020525f908152604090205460ff1681565b6102e46202a30081565b6103776103fa366004613e72565b61120e565b600d54610412906001600160a01b031681565b6040516102ee9190613ea0565b6102cf61042d366004613c75565b611251565b6102cf6112c7565b61044d610448366004613eb4565b6112da565b6040805192835263ffffffff9091166020830152016102ee565b600154610412906001600160a01b031681565b6102cf610488366004613c75565b611481565b6102e461049b366004613c90565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102e4565b6104126115bf565b600b54610412906001600160a01b031681565b6103776104eb366004613c75565b6115ed565b6105166104fe366004613c75565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ee565b6102e460035481565b610548610543366004613c90565b61160a565b6040516102ee9190613ee9565b610377610563366004613e72565b6116a0565b6102cf610576366004613c75565b6116e3565b6102e4600e5481565b610597610592366004613c90565b61177a565b6040516102ee93929190613efb565b6102cf6105b4366004613c75565b6118c5565b6102e46105c7366004613c90565b60086020525f908152604090205481565b6001546001600160a01b0316610412565b6102cf61193d565b6102e46105ff366004613c90565b5f9081526008602052604090205490565b6102cf61061e366004613f3d565b6119b9565b610377610631366004613c90565b611b16565b5f54610412906001600160a01b031681565b600c54610412906001600160a01b031681565b6102cf610669366004613c75565b611df9565b6102cf61067c366004613f67565b611e71565b6102e461068f366004613c90565b612034565b6102e4612065565b6106a4601481565b60405160ff90911681526020016102ee565b6102cf6106c4366004613c75565b612077565b6004546106e59064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ee565b610744610711366004613c90565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ee949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e460025481565b61041261078a366004613f67565b6120b1565b6107976115bf565b6001600160a01b0316336001600160a01b031614806107c057506001546001600160a01b031633145b6107dd57604051632864c4e160e01b815260040160405180910390fd5b6107e6816115ed565b819061080f576040516381e5828960e01b81526004016108069190613ea0565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff169061083d906004908361211e565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161086a83613f9b565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a60205260408120600481015461090a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61091c6123c0565b600c546001600160a01b0316156109455760405162035f5560e61b815260040160405180910390fd5b6001600160a01b03811661096c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383169081179091556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b905f90a250565b5f888152600a602052604090206002815460ff1660038111156109da576109da613fb0565b146109f857604051634f4b461f60e11b815260040160405180910390fd5b600481015415610a1b5760405163632a22bb60e01b815260040160405180910390fd5b85610a3957604051636caad1ed60e11b815260040160405180910390fd5b5f610a9d82600601805480602002602001604051908101604052809291908181526020018280548015610a9357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610a75575b50505050506123f2565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610aef573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b169190810190614140565b9050806101c0015115610b3357610b338b828a858b8b8b8b6124f8565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610b91575f5ffd5b505af1158015610ba3573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610be496959493929190614302565b60405180910390a25050505050505050505050565b610c016123c0565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610c7257610c72613fc4565b604051908082528060200260200182016040528015610c9b578160200160208202803683370190505b509450806001600160401b03811115610cb657610cb6613fc4565b604051908082528060200260200182016040528015610cdf578160200160208202803683370190505b5093505f805b83811015610ddb575f856006018281548110610d0357610d0361434f565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610d4957610d49613fb0565b03610dd25780888481518110610d6157610d6161434f565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610db957610db961434f565b602090810291909101015282610dce81614363565b9350505b50600101610ce5565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610e0957610e09613fb0565b03610e2757604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610e5c5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610e8057610e80613fb0565b14610e9e576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610ee5573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f09919061437b565b905080610f1c60408601602087016143a5565b63ffffffff161115610f3460408601602087016143a5565b829091610f62576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610806565b5050815460ff1916600190811783558201859055436002830155600354610f8990426143be565b6003830155610f9d60058301856002613b73565b50610fa6612065565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ff7928a928a92916143d1565b60405180910390a250600195945050505050565b5f611015826115ed565b61102057505f919050565b6001546001600160a01b0316611049576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790611079908590600401613ea0565b602060405180830381865afa158015611094573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b89190614421565b92915050565b6110c66123c0565b600d546001600160a01b0316806110f05760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461113157604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610806565b50505f6202a300600e5461114591906143be565b9050804281811015611173576040516337c8270b60e01b815260048101929092526024820152604401610806565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690555f600e8190556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b9190a2505050565b5f818152600a602052604081206001815460ff1660038111156111f4576111f4613fb0565b1461120157505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561124957611249613fb0565b149392505050565b6112596123c0565b6001600160a01b0381166112805760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b6112cf6123c0565b6112d85f6126f4565b565b600b545f9081906001600160a01b031633146113095760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff16600381111561132e5761132e613fb0565b1461134c57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561138c5761138c613fb0565b1461139c57600b01549150611479565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b82018054916113d083613f9b565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611421929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6114896115bf565b6001600160a01b0316336001600160a01b031614806114b257506001546001600160a01b031633145b6114cf57604051632864c4e160e01b815260040160405180910390fd5b6114d8816115ed565b6115bc5760048054600160281b900464ffffffffff1690611502906001600160a01b038416612764565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161155383614363565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016108cf565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a6020526040902060048101546060919061163d576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561169357602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611675575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156116da576116da613fb0565b14159392505050565b6116eb6123c0565b6001600160a01b0381166117125760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611766906202a300906143be565b60405190815260200160405180910390a250565b5f81815260096020526040902054606090819081906117ac576040516322e679e360e11b815260040160405180910390fd5b5f848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561181157602002820191905f5260205f20905b8154815260200190600101908083116117fd575b505050505092508180548060200260200160405190810160405280929190818152602001828054801561186157602002820191905f5260205f20905b81548152602001906001019080831161184d575b50505050509150808054806020026020016040519081016040528092919081815260200182805480156118b157602002820191905f5260205f20905b81548152602001906001019080831161189d575b505050505090509250925092509193909250565b6118cd6123c0565b6001600160a01b0381166118f45760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6119456123c0565b600d546001600160a01b03168061196f5760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690555f600e8190556040516001600160a01b038316917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a250565b5f6119c261293a565b805490915060ff600160401b82041615906001600160401b03165f811580156119e85750825b90505f826001600160401b03166001148015611a035750303b155b905081158015611a11575080155b15611a2f5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611a5957845460ff60401b1916600160401b1785555b6001600160a01b038716611a805760405163d92e233d60e01b815260040160405180910390fd5b611a8933612962565b611a9560046014612973565b611a9e86610bf9565b611aa66115bf565b6001600160a01b0316876001600160a01b031614611ac757611ac787612077565b8315611b0d57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff166003811115611b3a57611b3a613fb0565b03611b5857604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611b7057611b70613fb0565b14611b8e57604051631860f69960e31b815260040160405180910390fd5b80600301544211611bb257604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611c97578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611c78575f5ffd5b505af1158015611c8a573d5f5f3e3d5ffd5b505f979650505050505050565b611ca0826129f2565b815460ff191660021782556006820154600b83018190555f816001600160401b03811115611cd057611cd0613fc4565b604051908082528060200260200182016040528015611cf9578160200160208202803683370190505b5090505f5b82811015611d6b57846009015f866006018381548110611d2057611d2061434f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611d5857611d5861434f565b6020908102919091010152600101611cfe565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611dae575f5ffd5b505af1158015611dc0573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ff792919061443a565b611e016123c0565b6001600160a01b038116611e285760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611e9557611e95613fb0565b03611eb357604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611ecb57611ecb613fb0565b14611ee957604051631860f69960e31b815260040160405180910390fd5b8060030154421115611f0e57604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff1615611f405760405163257309f160e11b815260040160405180910390fd5b611f493361100b565b611f665760405163149fbcfd60e11b815260040160405180910390fd5b611f71338385612b18565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff19166001179055909150611ff090839083612ce9565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480612060576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61207260046014612eea565b905090565b61207f6123c0565b6001600160a01b0381166120a8575f604051631e4fbdf760e01b81526004016108069190613ea0565b6115bc816126f4565b5f828152600a60205260408120600601805483908082106120ee576040516326c5c55b60e11b815260048101929092526024820152604401610806565b50508083815481106121025761210261434f565b5f918252602090912001546001600160a01b0316949350505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001821061215d5760405162461bcd60e51b81526004016108069061444c565b825464ffffffffff600160281b909104811690821681116121bb5760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610806565b825f5b81866001015f6121ce8488612fe3565b64ffffffffff1681526020019081526020015f20819055505f8160016121f49190614496565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff16811161222957506123b8565b600185165f036122f0575f612248836122438860016144af565b612fe3565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916122a9916004016144cc565b602060405180830381865af41580156122c4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e8919061437b565b9350506123a4565b5f612300836122436001896144fc565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612361916004016144cc565b602060405180830381865af415801561237c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123a0919061437b565b9350505b50647fffffffff600194851c1693016121be565b505050505050565b336123c96115bf565b6001600160a01b0316146112d8573360405163118cdaa760e01b81526004016108069190613ea0565b80515f9081612402826014614519565b6001600160401b0381111561241957612419613fc4565b6040519080825280601f01601f191660200182016040528015612443576020820181803683370190505b5090505f5b828110156124e8575f8582815181106124635761246361434f565b602002602001015160601b90505f82601461247e9190614519565b90505f5b60148110156124da5782816014811061249d5761249d61434f565b1a60f81b856124ac83856143be565b815181106124bc576124bc61434f565b60200101906001600160f81b03191690815f1a905350600101612482565b505050806001019050612448565b5080516020909101209392505050565b8261251657604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b815260040161254d9493929190614530565b602060405180830381865afa158015612568573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061258c9190614421565b6125a95760405163051d8aa760e51b815260040160405180910390fd5b806125c757604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b03166125f057604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b815260040161263f979695949392919061454f565b5f60405180830381865afa158015612659573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526126809190810190614629565b5f8e8152600f6020908152604090912084519497509295509093506126a89290860190613c14565b505f8b815260106020908152604090912083516126c792850190613c14565b505f8b815260116020908152604090912082516126e692840190613c14565b505050505050505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106127b35760405162461bcd60e51b81526004016108069061444c565b825464ffffffffff908116908216106128065760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610806565b6128118160016144af565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6128488487612fe3565b64ffffffffff16815260208101919091526040015f20556001831615612933575f612878826122436001876144fc565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916128d9916004016144cc565b602060405180830381865af41580156128f4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612918919061437b565b647fffffffff600195861c1694909350919091019050612838565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006110b8565b61296a613000565b6115bc81613025565b602060ff821611156129c15760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610806565b6129d2600160ff831681901b61470d565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612b13575f612a0d8260016143be565b90505b82811015612b0a575f846006018381548110612a2e57612a2e61434f565b5f9182526020822001546006870180546001600160a01b0390921693509084908110612a5c57612a5c61434f565b5f918252602090912001546001600160a01b0390811691508216811015612b005780866006018581548110612a9357612a9361434f565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612ad457612ad461434f565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612a10565b506001016129f9565b505050565b5f8211612b385760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612b61576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612b979161470d565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612bde573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c02919061437b565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c55573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c79919061437b565b90505f8111612c9b5760405163aeaddff160e01b815260040160405180910390fd5b5f612ca68284614720565b90505f8111612cc85760405163149fbcfd60e11b815260040160405180910390fd5b80861115611b0d5760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612d6757508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612ee3565b5f5f90505f876009015f855f81548110612d8357612d8361434f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612e0b575f896009015f878481548110612dcd57612dcd61434f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e02578092508193505b50600101612dac565b50808610612e1f575f945050505050612ee3565b5f88600a015f868581548110612e3757612e3761434f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612e7457612e74613fb0565b021790555086848381548110612e8c57612e8c61434f565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612f3d5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610806565b602060ff83161115612f615760405162461bcd60e51b81526004016108069061473f565b8254600160281b900464ffffffffff1680612f8060ff85166002614890565b64ffffffffff161015612fd05760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610806565b612fdb84828561302d565b949350505050565b5f81612ff660ff851663ffffffff6148a9565b612ee391906144af565b6130086130f5565b6112d857604051631afcd79f60e31b815260040160405180910390fd5b61207f613000565b5f602060ff831611156130525760405162461bcd60e51b81526004016108069061473f565b8264ffffffffff165f03613070576130698261310e565b9050612ee3565b5f61307c836001614496565b60ff166001600160401b0381111561309657613096613fc4565b6040519080825280602002602001820160405280156130bf578160200160208202803683370190505b5090506130ce858585846137a8565b808360ff16815181106130e3576130e361434f565b60200260200101519150509392505050565b5f6130fe61293a565b54600160401b900460ff16919050565b5f8160ff165f0361312057505f919050565b8160ff1660010361315257507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361318457507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036131b657507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036131e857507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361321a57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361324c57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361327e57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036132b057507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036132e257507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361331457507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361334657507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361337857507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036133aa57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036133dc57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361340e57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361344057507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361347257507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff166012036134a457507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036134d657507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361350857507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361353a57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361356c57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361359e57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036135d057507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361360257507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361363457507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361366657507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361369857507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036136ca57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036136fc57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361372e57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361376057507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610806565b602060ff831611156137cc5760405162461bcd60e51b81526004016108069061473f565b5f8364ffffffffff16116138305760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610806565b5f61383c6001856144fc565b9050600181165f0361388f57846001015f6138575f84612fe3565b64ffffffffff1681526020019081526020015f2054825f8151811061387e5761387e61434f565b6020026020010181815250506138b7565b6138985f61310e565b825f815181106138aa576138aa61434f565b6020026020010181815250505b5f5b8360ff168160ff1610156123b857600182165f036139af5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff168151811061390b5761390b61434f565b602002602001015181526020016139218561310e565b8152506040518263ffffffff1660e01b815260040161394091906144cc565b602060405180830381865af415801561395b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061397f919061437b565b8361398b836001614496565b60ff168151811061399e5761399e61434f565b602002602001018181525050613b60565b5f6139bb826001614496565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613a5d575f876001015f613a12856001613a019190614496565b60018864ffffffffff16901c612fe3565b64ffffffffff1681526020019081526020015f205490508085846001613a389190614496565b60ff1681518110613a4b57613a4b61434f565b60200260200101818152505050613b5e565b5f876001015f613a748560018861224391906144fc565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613acb57613acb61434f565b60200260200101518152506040518263ffffffff1660e01b8152600401613af291906144cc565b602060405180830381865af4158015613b0d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b31919061437b565b85613b3d856001614496565b60ff1681518110613b5057613b5061434f565b602002602001018181525050505b505b647fffffffff600192831c1691016138b9565b600183019183908215613c04579160200282015f5b83821115613bd257833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613b88565b8015613c025782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613bd2565b505b50613c10929150613c4d565b5090565b828054828255905f5260205f20908101928215613c04579160200282015b82811115613c04578251825591602001919060010190613c32565b5b80821115613c10575f8155600101613c4e565b6001600160a01b03811681146115bc575f5ffd5b5f60208284031215613c85575f5ffd5b8135612ee381613c61565b5f60208284031215613ca0575f5ffd5b5035919050565b5f5f83601f840112613cb7575f5ffd5b5081356001600160401b03811115613ccd575f5ffd5b602083019150836020828501011115613ce4575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613d02575f5ffd5b8835975060208901356001600160401b03811115613d1e575f5ffd5b613d2a8b828c01613ca7565b9098509650506040890135945060608901356001600160401b03811115613d4f575f5ffd5b613d5b8b828c01613ca7565b90955093505060808901356001600160401b03811115613d79575f5ffd5b613d858b828c01613ca7565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613dd25781516001600160a01b0316865260209586019590910190600101613dab565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613dd2578151865260209586019590910190600101613dee565b604081525f613e1e6040830185613d99565b8281036020840152613e308185613ddc565b95945050505050565b5f5f5f60808486031215613e4b575f5ffd5b833592506020840135915060808401851015613e65575f5ffd5b6040840190509250925092565b5f5f60408385031215613e83575f5ffd5b823591506020830135613e9581613c61565b809150509250929050565b6001600160a01b0391909116815260200190565b5f5f5f60608486031215613ec6575f5ffd5b833592506020840135613ed881613c61565b929592945050506040919091013590565b602081525f612ee36020830184613d99565b606081525f613f0d6060830186613ddc565b8281036020840152613f1f8186613ddc565b90508281036040840152613f338185613ddc565b9695505050505050565b5f5f60408385031215613f4e575f5ffd5b8235613f5981613c61565b946020939093013593505050565b5f5f60408385031215613f78575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f81613fa957613fa9613f87565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b0381118282101715613ffb57613ffb613fc4565b60405290565b604051601f8201601f191681016001600160401b038111828210171561402957614029613fc4565b604052919050565b805160048110612060575f5ffd5b5f82601f83011261404e575f5ffd5b604080519081016001600160401b038111828210171561407057614070613fc4565b8060405250806040840185811115614086575f5ffd5b845b818110156140a0578051835260209283019201614088565b509195945050505050565b805161206081613c61565b805160ff81168114612060575f5ffd5b5f82601f8301126140d5575f5ffd5b81516001600160401b038111156140ee576140ee613fc4565b614101601f8201601f1916602001614001565b818152846020838601011115614115575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b80518015158114612060575f5ffd5b5f60208284031215614150575f5ffd5b81516001600160401b03811115614165575f5ffd5b82016102008185031215614177575f5ffd5b61417f613fd8565b8151815261418f60208301614031565b6020820152604082810151908201526141ab856060840161403f565b606082015260a082015160808201526141c660c083016140ab565b60a08201526141d760e083016140b6565b60c08201526101008201516001600160401b038111156141f5575f5ffd5b614201868285016140c6565b60e08301525061421461012083016140ab565b61010082015261422761014083016140ab565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561425d575f5ffd5b614269868285016140c6565b6101808301525061427d6101c083016140ab565b6101a08201526142906101e08301614131565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613dd25781546001600160a01b03168652602090950194600191820191016142b3565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f614314608083018961429e565b828103602084015261432781888a6142da565b905085604084015282810360608401526143428185876142da565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161437457614374613f87565b5060010190565b5f6020828403121561438b575f5ffd5b5051919050565b803563ffffffff81168114612060575f5ffd5b5f602082840312156143b5575f5ffd5b612ee382614392565b808201808211156110b8576110b8613f87565b84815260a0810160208201855f5b600281101561440c5763ffffffff6143f683614392565b16835260209283019291909101906001016143df565b50505060608201939093526080015292915050565b5f60208284031215614431575f5ffd5b612ee382614131565b604081525f613e1e604083018561429e565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff81811683821601908111156110b8576110b8613f87565b64ffffffffff81811683821601908111156110b8576110b8613f87565b6040810181835f5b60028110156144f35781518352602092830192909101906001016144d4565b50505092915050565b64ffffffffff82811682821603908111156110b8576110b8613f87565b80820281158282048414176110b8576110b8613f87565b848152836020820152606060408201525f613f336060830184866142da565b60018060a01b038816815286602082015285604082015260a060608201525f61457c60a0830186886142da565b828103608084015261458f8185876142da565b9a9950505050505050505050565b5f6001600160401b038211156145b5576145b5613fc4565b5060051b60200190565b5f82601f8301126145ce575f5ffd5b81516145e16145dc8261459d565b614001565b8082825260208201915060208360051b860101925085831115614602575f5ffd5b602085015b8381101561461f578051835260209283019201614607565b5095945050505050565b5f5f5f6060848603121561463b575f5ffd5b83516001600160401b03811115614650575f5ffd5b8401601f81018613614660575f5ffd5b805161466e6145dc8261459d565b8082825260208201915060208360051b85010192508883111561468f575f5ffd5b6020840193505b828410156146b1578351825260209384019390910190614696565b8096505050505060208401516001600160401b038111156146d0575f5ffd5b6146dc868287016145bf565b92505060408401516001600160401b038111156146f7575f5ffd5b614703868287016145bf565b9150509250925092565b818103818111156110b8576110b8613f87565b5f8261473a57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115611479578085048111156147a1576147a1613f87565b60018416156147af57908102905b60019390931c928002614786565b5f826147cb575060016110b8565b816147d757505f6110b8565b81600181146147ed57600281146147f757614829565b60019150506110b8565b60ff84111561480857614808613f87565b6001841b915064ffffffffff82111561482357614823613f87565b506110b8565b5060208310610133831016604e8410600b8410161715614861575081810a64ffffffffff81111561485c5761485c613f87565b6110b8565b61487164ffffffffff8484614782565b8064ffffffffff0482111561488857614888613f87565b029392505050565b5f612ee364ffffffffff841664ffffffffff84166147bd565b64ffffffffff81811683821602908116908181146148c9576148c9613f87565b509291505056fea164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b61497a806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102b8575f3560e01c80639a7a2ffc11610177578063da881e5a116100d5578063e82f3b701161008f578063e82f3b7014610694578063ebf0c717146106a7578063f1650536146106af578063f2fde38b146106c9578063f379b0df146106dc578063f52fd80314610716578063f6fc05d514610786575f5ffd5b8063da881e5a14610623578063dbb06c9314610636578063e4be6e3d14610648578063e4d185db1461065b578063e59e46951461066e578063e6745e1314610681575f5ffd5b8063b8ab470411610131578063b8ab470414610584578063bff232c1146105a6578063c2b40ae4146105b9578063c3a0ec30146105d8578063c8fe182d146105e9578063ca2869a0146105f1578063cd6dc68714610610575f5ffd5b80639a7a2ffc146104f05780639f0f874a1461052c578063a016493014610535578063a8a4d69b14610555578063acc5249414610568578063b2d5d1ac1461057b575f5ffd5b806350e6fad31161022457806385814243116101de57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ac5780638da5cb5b146104c25780638e5ce3ad146104ca5780639015d371146104dd575f5ffd5b806350e6fad3146103e25780635d504776146103ec57806362cc89a8146103ff57806370e36bbe1461041f578063715018a6146104325780637c92f5241461043a575f5ffd5b80632800d829116102755780632800d82914610351578063291a691b146103645780632e7b716d14610387578063323beaa51461039a5780634d6861a6146103ad57806350e6d94c146103c0575f5ffd5b8063096b810a146102bc578063099a161a146102d15780630b45f8e9146102f75780630bbfade71461030a5780630f3e34121461031d57806317d6112014610330575b5f5ffd5b6102cf6102ca366004613d12565b61078f565b005b6102e46102df366004613d2d565b6108db565b6040519081526020015b60405180910390f35b6102cf610305366004613d12565b610914565b6102cf610318366004613d88565b610a19565b6102cf61032b366004613d2d565b610c5d565b61034361033e366004613d2d565b610ca0565b6040516102ee929190613ea9565b6102e461035f366004613d2d565b610e49565b610377610372366004613ed6565b610e95565b60405190151581526020016102ee565b610377610395366004613d12565b61106f565b6102cf6103a8366004613d12565b611122565b6103776103bb366004613d2d565b611233565b6103776103ce366004613d12565b60066020525f908152604090205460ff1681565b6102e46202a30081565b6103776103fa366004613f0f565b611272565b600d54610412906001600160a01b031681565b6040516102ee9190613f3d565b6102cf61042d366004613d12565b6112b5565b6102cf61132b565b61044d610448366004613f51565b61133e565b6040805192835263ffffffff9091166020830152016102ee565b600154610412906001600160a01b031681565b6102cf610488366004613d12565b6114e5565b6102e461049b366004613d2d565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102e4565b610412611623565b600b54610412906001600160a01b031681565b6103776104eb366004613d12565b611651565b6105166104fe366004613d12565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ee565b6102e460035481565b610548610543366004613d2d565b61166e565b6040516102ee9190613f86565b610377610563366004613f0f565b611704565b6102cf610576366004613d12565b611747565b6102e4600e5481565b610597610592366004613d2d565b6117de565b6040516102ee93929190613f98565b6102cf6105b4366004613d12565b611929565b6102e46105c7366004613d2d565b60086020525f908152604090205481565b6001546001600160a01b0316610412565b6102cf6119a1565b6102e46105ff366004613d2d565b5f9081526008602052604090205490565b6102cf61061e366004613fda565b611a1d565b610377610631366004613d2d565b611b7a565b5f54610412906001600160a01b031681565b600c54610412906001600160a01b031681565b610412610669366004614004565b611e5d565b6102cf61067c366004613d12565b611f03565b6102cf61068f366004614004565b611f7b565b6102e46106a2366004613d2d565b61213e565b6102e461216f565b6106b7601481565b60405160ff90911681526020016102ee565b6102cf6106d7366004613d12565b612181565b6004546106f89064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ee565b610757610724366004613d2d565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ee949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e460025481565b610797611623565b6001600160a01b0316336001600160a01b031614806107c057506001546001600160a01b031633145b6107dd57604051632864c4e160e01b815260040160405180910390fd5b6107e681611651565b819061080f576040516381e5828960e01b81526004016108069190613f3d565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff169061083d90600490836121bb565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161086a83614038565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a60205260408120600481015461090a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61091c61245d565b600c546001600160a01b0316156109455760405162035f5560e61b815260040160405180910390fd5b6001600160a01b03811661096c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d5416156109e357600d80546001600160a01b031981169091555f600e8190556040516001600160a01b039092169182917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a2505b6040516001600160a01b038216907f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b905f90a250565b5f888152600a602052604090206002815460ff166003811115610a3e57610a3e61404d565b14610a5c57604051634f4b461f60e11b815260040160405180910390fd5b600481015415610a7f5760405163632a22bb60e01b815260040160405180910390fd5b85610a9d57604051636caad1ed60e11b815260040160405180910390fd5b5f610b0182600601805480602002602001604051908101604052809291908181526020018280548015610af757602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610ad9575b505050505061248f565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610b53573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b7a91908101906141dd565b9050806101c0015115610b9757610b978b828a858b8b8b8b612595565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610bf5575f5ffd5b505af1158015610c07573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610c489695949392919061439f565b60405180910390a25050505050505050505050565b610c6561245d565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610cd657610cd6614061565b604051908082528060200260200182016040528015610cff578160200160208202803683370190505b509450806001600160401b03811115610d1a57610d1a614061565b604051908082528060200260200182016040528015610d43578160200160208202803683370190505b5093505f805b83811015610e3f575f856006018281548110610d6757610d676143ec565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610dad57610dad61404d565b03610e365780888481518110610dc557610dc56143ec565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610e1d57610e1d6143ec565b602090810291909101015282610e3281614400565b9350505b50600101610d49565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610e6d57610e6d61404d565b03610e8b57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610ec05760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610ee457610ee461404d565b14610f02576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610f49573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f6d9190614418565b905080610f806040860160208701614442565b63ffffffff161115610f986040860160208701614442565b829091610fc6576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610806565b5050815460ff1916600190811783558201859055436002830155600354610fed904261445b565b600383015561100160058301856002613c10565b5061100a61216f565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a1709261105b928a928a929161446e565b60405180910390a250600195945050505050565b5f61107982611651565b61108457505f919050565b6001546001600160a01b03166110ad576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906110dd908590600401613f3d565b602060405180830381865afa1580156110f8573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061111c91906144be565b92915050565b61112a61245d565b600d546001600160a01b0316806111545760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461119557604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610806565b50505f6202a300600e546111a9919061445b565b90508042818110156111d7576040516337c8270b60e01b815260048101929092526024820152604401610806565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690555f600e8190556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b9190a2505050565b5f818152600a602052604081206001815460ff1660038111156112585761125861404d565b1461126557505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156112ad576112ad61404d565b149392505050565b6112bd61245d565b6001600160a01b0381166112e45760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61133361245d565b61133c5f612791565b565b600b545f9081906001600160a01b0316331461136d5760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff1660038111156113925761139261404d565b146113b057604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff1660028111156113f0576113f061404d565b1461140057600b015491506114dd565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b820180549161143483614038565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611485929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6114ed611623565b6001600160a01b0316336001600160a01b0316148061151657506001546001600160a01b031633145b61153357604051632864c4e160e01b815260040160405180910390fd5b61153c81611651565b6116205760048054600160281b900464ffffffffff1690611566906001600160a01b038416612801565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916115b783614400565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016108cf565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906116a1576040516322e679e360e11b815260040160405180910390fd5b806006018054806020026020016040519081016040528092919081815260200182805480156116f757602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116116d9575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561173e5761173e61404d565b14159392505050565b61174f61245d565b6001600160a01b0381166117765760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f25906117ca906202a3009061445b565b60405190815260200160405180910390a250565b5f8181526009602052604090205460609081908190611810576040516322e679e360e11b815260040160405180910390fd5b5f848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561187557602002820191905f5260205f20905b815481526020019060010190808311611861575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156118c557602002820191905f5260205f20905b8154815260200190600101908083116118b1575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561191557602002820191905f5260205f20905b815481526020019060010190808311611901575b505050505090509250925092509193909250565b61193161245d565b6001600160a01b0381166119585760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6119a961245d565b600d546001600160a01b0316806119d35760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690555f600e8190556040516001600160a01b038316917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a250565b5f611a266129d7565b805490915060ff600160401b82041615906001600160401b03165f81158015611a4c5750825b90505f826001600160401b03166001148015611a675750303b155b905081158015611a75575080155b15611a935760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611abd57845460ff60401b1916600160401b1785555b6001600160a01b038716611ae45760405163d92e233d60e01b815260040160405180910390fd5b611aed336129ff565b611af960046014612a10565b611b0286610c5d565b611b0a611623565b6001600160a01b0316876001600160a01b031614611b2b57611b2b87612181565b8315611b7157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff166003811115611b9e57611b9e61404d565b03611bbc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611bd457611bd461404d565b14611bf257604051631860f69960e31b815260040160405180910390fd5b80600301544211611c1657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611cfb578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611cdc575f5ffd5b505af1158015611cee573d5f5f3e3d5ffd5b505f979650505050505050565b611d0482612a8f565b815460ff191660021782556006820154600b83018190555f816001600160401b03811115611d3457611d34614061565b604051908082528060200260200182016040528015611d5d578160200160208202803683370190505b5090505f5b82811015611dcf57846009015f866006018381548110611d8457611d846143ec565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611dbc57611dbc6143ec565b6020908102919091010152600101611d62565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611e12575f5ffd5b505af1158015611e24573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d7856006018360405161105b9291906144d7565b5f828152600a602052604081206002815460ff166003811115611e8257611e8261404d565b14611ea057604051634f4b461f60e11b815260040160405180910390fd5b60068101548390808210611ed0576040516326c5c55b60e11b815260048101929092526024820152604401610806565b5050806006018381548110611ee757611ee76143ec565b5f918252602090912001546001600160a01b0316949350505050565b611f0b61245d565b6001600160a01b038116611f325760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611f9f57611f9f61404d565b03611fbd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611fd557611fd561404d565b14611ff357604051631860f69960e31b815260040160405180910390fd5b806003015442111561201857604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff161561204a5760405163257309f160e11b815260040160405180910390fd5b6120533361106f565b6120705760405163149fbcfd60e11b815260040160405180910390fd5b61207b338385612bb5565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff191660011790559091506120fa90839083612d86565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f818152600960205260409020548061216a576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61217c60046014612f87565b905090565b61218961245d565b6001600160a01b0381166121b2575f604051631e4fbdf760e01b81526004016108069190613f3d565b61162081612791565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106121fa5760405162461bcd60e51b8152600401610806906144e9565b825464ffffffffff600160281b909104811690821681116122585760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610806565b825f5b81866001015f61226b8488613080565b64ffffffffff1681526020019081526020015f20819055505f8160016122919190614533565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116122c65750612455565b600185165f0361238d575f6122e5836122e088600161454c565b613080565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161234691600401614569565b602060405180830381865af4158015612361573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123859190614418565b935050612441565b5f61239d836122e0600189614599565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916123fe91600401614569565b602060405180830381865af4158015612419573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061243d9190614418565b9350505b50647fffffffff600194851c16930161225b565b505050505050565b33612466611623565b6001600160a01b03161461133c573360405163118cdaa760e01b81526004016108069190613f3d565b80515f908161249f8260146145b6565b6001600160401b038111156124b6576124b6614061565b6040519080825280601f01601f1916602001820160405280156124e0576020820181803683370190505b5090505f5b82811015612585575f858281518110612500576125006143ec565b602002602001015160601b90505f82601461251b91906145b6565b90505f5b60148110156125775782816014811061253a5761253a6143ec565b1a60f81b85612549838561445b565b81518110612559576125596143ec565b60200101906001600160f81b03191690815f1a90535060010161251f565b5050508060010190506124e5565b5080516020909101209392505050565b826125b357604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b81526004016125ea94939291906145cd565b602060405180830381865afa158015612605573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061262991906144be565b6126465760405163051d8aa760e51b815260040160405180910390fd5b8061266457604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661268d57604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b81526004016126dc97969594939291906145ec565b5f60405180830381865afa1580156126f6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261271d91908101906146c6565b5f8e8152600f6020908152604090912084519497509295509093506127459290860190613cb1565b505f8b8152601060209081526040909120835161276492850190613cb1565b505f8b8152601160209081526040909120825161278392840190613cb1565b505050505050505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106128505760405162461bcd60e51b8152600401610806906144e9565b825464ffffffffff908116908216106128a35760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610806565b6128ae81600161454c565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6128e58487613080565b64ffffffffff16815260208101919091526040015f205560018316156129d0575f612915826122e0600187614599565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161297691600401614569565b602060405180830381865af4158015612991573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129b59190614418565b647fffffffff600195861c16949093509190910190506128d5565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061111c565b612a0761309d565b611620816130c2565b602060ff82161115612a5e5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610806565b612a6f600160ff831681901b6147aa565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612bb0575f612aaa82600161445b565b90505b82811015612ba7575f846006018381548110612acb57612acb6143ec565b5f9182526020822001546006870180546001600160a01b0390921693509084908110612af957612af96143ec565b5f918252602090912001546001600160a01b0390811691508216811015612b9d5780866006018581548110612b3057612b306143ec565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612b7157612b716143ec565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612aad565b50600101612a96565b505050565b5f8211612bd55760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612bfe576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612c34916147aa565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612c7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c9f9190614418565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cf2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d169190614418565b90505f8111612d385760405163aeaddff160e01b815260040160405180910390fd5b5f612d4382846147bd565b90505f8111612d655760405163149fbcfd60e11b815260040160405180910390fd5b80861115611b715760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612e0457508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612f80565b5f5f90505f876009015f855f81548110612e2057612e206143ec565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612ea8575f896009015f878481548110612e6a57612e6a6143ec565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e9f578092508193505b50600101612e49565b50808610612ebc575f945050505050612f80565b5f88600a015f868581548110612ed457612ed46143ec565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612f1157612f1161404d565b021790555086848381548110612f2957612f296143ec565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612fda5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610806565b602060ff83161115612ffe5760405162461bcd60e51b8152600401610806906147dc565b8254600160281b900464ffffffffff168061301d60ff8516600261492d565b64ffffffffff16101561306d5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610806565b6130788482856130ca565b949350505050565b5f8161309360ff851663ffffffff614946565b612f80919061454c565b6130a5613192565b61133c57604051631afcd79f60e31b815260040160405180910390fd5b61218961309d565b5f602060ff831611156130ef5760405162461bcd60e51b8152600401610806906147dc565b8264ffffffffff165f0361310d57613106826131ab565b9050612f80565b5f613119836001614533565b60ff166001600160401b0381111561313357613133614061565b60405190808252806020026020018201604052801561315c578160200160208202803683370190505b50905061316b85858584613845565b808360ff1681518110613180576131806143ec565b60200260200101519150509392505050565b5f61319b6129d7565b54600160401b900460ff16919050565b5f8160ff165f036131bd57505f919050565b8160ff166001036131ef57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361322157507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361325357507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361328557507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036132b757507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036132e957507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361331b57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361334d57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361337f57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036133b157507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036133e357507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361341557507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361344757507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361347957507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036134ab57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036134dd57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361350f57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361354157507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361357357507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036135a557507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036135d757507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361360957507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361363b57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361366d57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361369f57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036136d157507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361370357507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361373557507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361376757507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361379957507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f036137cb57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036137fd57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610806565b602060ff831611156138695760405162461bcd60e51b8152600401610806906147dc565b5f8364ffffffffff16116138cd5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610806565b5f6138d9600185614599565b9050600181165f0361392c57846001015f6138f45f84613080565b64ffffffffff1681526020019081526020015f2054825f8151811061391b5761391b6143ec565b602002602001018181525050613954565b6139355f6131ab565b825f81518110613947576139476143ec565b6020026020010181815250505b5f5b8360ff168160ff16101561245557600182165f03613a4c5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff16815181106139a8576139a86143ec565b602002602001015181526020016139be856131ab565b8152506040518263ffffffff1660e01b81526004016139dd9190614569565b602060405180830381865af41580156139f8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613a1c9190614418565b83613a28836001614533565b60ff1681518110613a3b57613a3b6143ec565b602002602001018181525050613bfd565b5f613a58826001614533565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613afa575f876001015f613aaf856001613a9e9190614533565b60018864ffffffffff16901c613080565b64ffffffffff1681526020019081526020015f205490508085846001613ad59190614533565b60ff1681518110613ae857613ae86143ec565b60200260200101818152505050613bfb565b5f876001015f613b11856001886122e09190614599565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613b6857613b686143ec565b60200260200101518152506040518263ffffffff1660e01b8152600401613b8f9190614569565b602060405180830381865af4158015613baa573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bce9190614418565b85613bda856001614533565b60ff1681518110613bed57613bed6143ec565b602002602001018181525050505b505b647fffffffff600192831c169101613956565b600183019183908215613ca1579160200282015f5b83821115613c6f57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613c25565b8015613c9f5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613c6f565b505b50613cad929150613cea565b5090565b828054828255905f5260205f20908101928215613ca1579160200282015b82811115613ca1578251825591602001919060010190613ccf565b5b80821115613cad575f8155600101613ceb565b6001600160a01b0381168114611620575f5ffd5b5f60208284031215613d22575f5ffd5b8135612f8081613cfe565b5f60208284031215613d3d575f5ffd5b5035919050565b5f5f83601f840112613d54575f5ffd5b5081356001600160401b03811115613d6a575f5ffd5b602083019150836020828501011115613d81575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613d9f575f5ffd5b8835975060208901356001600160401b03811115613dbb575f5ffd5b613dc78b828c01613d44565b9098509650506040890135945060608901356001600160401b03811115613dec575f5ffd5b613df88b828c01613d44565b90955093505060808901356001600160401b03811115613e16575f5ffd5b613e228b828c01613d44565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613e6f5781516001600160a01b0316865260209586019590910190600101613e48565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613e6f578151865260209586019590910190600101613e8b565b604081525f613ebb6040830185613e36565b8281036020840152613ecd8185613e79565b95945050505050565b5f5f5f60808486031215613ee8575f5ffd5b833592506020840135915060808401851015613f02575f5ffd5b6040840190509250925092565b5f5f60408385031215613f20575f5ffd5b823591506020830135613f3281613cfe565b809150509250929050565b6001600160a01b0391909116815260200190565b5f5f5f60608486031215613f63575f5ffd5b833592506020840135613f7581613cfe565b929592945050506040919091013590565b602081525f612f806020830184613e36565b606081525f613faa6060830186613e79565b8281036020840152613fbc8186613e79565b90508281036040840152613fd08185613e79565b9695505050505050565b5f5f60408385031215613feb575f5ffd5b8235613ff681613cfe565b946020939093013593505050565b5f5f60408385031215614015575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161404657614046614024565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b038111828210171561409857614098614061565b60405290565b604051601f8201601f191681016001600160401b03811182821017156140c6576140c6614061565b604052919050565b80516004811061216a575f5ffd5b5f82601f8301126140eb575f5ffd5b604080519081016001600160401b038111828210171561410d5761410d614061565b8060405250806040840185811115614123575f5ffd5b845b8181101561413d578051835260209283019201614125565b509195945050505050565b805161216a81613cfe565b805160ff8116811461216a575f5ffd5b5f82601f830112614172575f5ffd5b81516001600160401b0381111561418b5761418b614061565b61419e601f8201601f191660200161409e565b8181528460208386010111156141b2575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b8051801515811461216a575f5ffd5b5f602082840312156141ed575f5ffd5b81516001600160401b03811115614202575f5ffd5b82016102008185031215614214575f5ffd5b61421c614075565b8151815261422c602083016140ce565b60208201526040828101519082015261424885606084016140dc565b606082015260a0820151608082015261426360c08301614148565b60a082015261427460e08301614153565b60c08201526101008201516001600160401b03811115614292575f5ffd5b61429e86828501614163565b60e0830152506142b16101208301614148565b6101008201526142c46101408301614148565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b038111156142fa575f5ffd5b61430686828501614163565b6101808301525061431a6101c08301614148565b6101a082015261432d6101e083016141ce565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613e6f5781546001600160a01b0316865260209095019460019182019101614350565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f6143b1608083018961433b565b82810360208401526143c481888a614377565b905085604084015282810360608401526143df818587614377565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161441157614411614024565b5060010190565b5f60208284031215614428575f5ffd5b5051919050565b803563ffffffff8116811461216a575f5ffd5b5f60208284031215614452575f5ffd5b612f808261442f565b8082018082111561111c5761111c614024565b84815260a0810160208201855f5b60028110156144a95763ffffffff6144938361442f565b168352602092830192919091019060010161447c565b50505060608201939093526080015292915050565b5f602082840312156144ce575f5ffd5b612f80826141ce565b604081525f613ebb604083018561433b565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff818116838216019081111561111c5761111c614024565b64ffffffffff818116838216019081111561111c5761111c614024565b6040810181835f5b6002811015614590578151835260209283019290910190600101614571565b50505092915050565b64ffffffffff828116828216039081111561111c5761111c614024565b808202811582820484141761111c5761111c614024565b848152836020820152606060408201525f613fd0606083018486614377565b60018060a01b038816815286602082015285604082015260a060608201525f61461960a083018688614377565b828103608084015261462c818587614377565b9a9950505050505050505050565b5f6001600160401b0382111561465257614652614061565b5060051b60200190565b5f82601f83011261466b575f5ffd5b815161467e6146798261463a565b61409e565b8082825260208201915060208360051b86010192508583111561469f575f5ffd5b602085015b838110156146bc5780518352602092830192016146a4565b5095945050505050565b5f5f5f606084860312156146d8575f5ffd5b83516001600160401b038111156146ed575f5ffd5b8401601f810186136146fd575f5ffd5b805161470b6146798261463a565b8082825260208201915060208360051b85010192508883111561472c575f5ffd5b6020840193505b8284101561474e578351825260209384019390910190614733565b8096505050505060208401516001600160401b0381111561476d575f5ffd5b6147798682870161465c565b92505060408401516001600160401b03811115614794575f5ffd5b6147a08682870161465c565b9150509250925092565b8181038181111561111c5761111c614024565b5f826147d757634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156114dd5780850481111561483e5761483e614024565b600184161561484c57908102905b60019390931c928002614823565b5f826148685750600161111c565b8161487457505f61111c565b816001811461488a5760028114614894576148c6565b600191505061111c565b60ff8411156148a5576148a5614024565b6001841b915064ffffffffff8211156148c0576148c0614024565b5061111c565b5060208310610133831016604e8410600b84101617156148fe575081810a64ffffffffff8111156148f9576148f9614024565b61111c565b61490e64ffffffffff848461481f565b8064ffffffffff0482111561492557614925614024565b029392505050565b5f612f8064ffffffffff841664ffffffffff841661485a565b64ffffffffff818116838216029081169081811461496657614966614024565b509291505056fea164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102b8575f3560e01c80639a7a2ffc11610177578063da881e5a116100d5578063e82f3b701161008f578063e82f3b7014610694578063ebf0c717146106a7578063f1650536146106af578063f2fde38b146106c9578063f379b0df146106dc578063f52fd80314610716578063f6fc05d514610786575f5ffd5b8063da881e5a14610623578063dbb06c9314610636578063e4be6e3d14610648578063e4d185db1461065b578063e59e46951461066e578063e6745e1314610681575f5ffd5b8063b8ab470411610131578063b8ab470414610584578063bff232c1146105a6578063c2b40ae4146105b9578063c3a0ec30146105d8578063c8fe182d146105e9578063ca2869a0146105f1578063cd6dc68714610610575f5ffd5b80639a7a2ffc146104f05780639f0f874a1461052c578063a016493014610535578063a8a4d69b14610555578063acc5249414610568578063b2d5d1ac1461057b575f5ffd5b806350e6fad31161022457806385814243116101de57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ac5780638da5cb5b146104c25780638e5ce3ad146104ca5780639015d371146104dd575f5ffd5b806350e6fad3146103e25780635d504776146103ec57806362cc89a8146103ff57806370e36bbe1461041f578063715018a6146104325780637c92f5241461043a575f5ffd5b80632800d829116102755780632800d82914610351578063291a691b146103645780632e7b716d14610387578063323beaa51461039a5780634d6861a6146103ad57806350e6d94c146103c0575f5ffd5b8063096b810a146102bc578063099a161a146102d15780630b45f8e9146102f75780630bbfade71461030a5780630f3e34121461031d57806317d6112014610330575b5f5ffd5b6102cf6102ca366004613d12565b61078f565b005b6102e46102df366004613d2d565b6108db565b6040519081526020015b60405180910390f35b6102cf610305366004613d12565b610914565b6102cf610318366004613d88565b610a19565b6102cf61032b366004613d2d565b610c5d565b61034361033e366004613d2d565b610ca0565b6040516102ee929190613ea9565b6102e461035f366004613d2d565b610e49565b610377610372366004613ed6565b610e95565b60405190151581526020016102ee565b610377610395366004613d12565b61106f565b6102cf6103a8366004613d12565b611122565b6103776103bb366004613d2d565b611233565b6103776103ce366004613d12565b60066020525f908152604090205460ff1681565b6102e46202a30081565b6103776103fa366004613f0f565b611272565b600d54610412906001600160a01b031681565b6040516102ee9190613f3d565b6102cf61042d366004613d12565b6112b5565b6102cf61132b565b61044d610448366004613f51565b61133e565b6040805192835263ffffffff9091166020830152016102ee565b600154610412906001600160a01b031681565b6102cf610488366004613d12565b6114e5565b6102e461049b366004613d2d565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102e4565b610412611623565b600b54610412906001600160a01b031681565b6103776104eb366004613d12565b611651565b6105166104fe366004613d12565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ee565b6102e460035481565b610548610543366004613d2d565b61166e565b6040516102ee9190613f86565b610377610563366004613f0f565b611704565b6102cf610576366004613d12565b611747565b6102e4600e5481565b610597610592366004613d2d565b6117de565b6040516102ee93929190613f98565b6102cf6105b4366004613d12565b611929565b6102e46105c7366004613d2d565b60086020525f908152604090205481565b6001546001600160a01b0316610412565b6102cf6119a1565b6102e46105ff366004613d2d565b5f9081526008602052604090205490565b6102cf61061e366004613fda565b611a1d565b610377610631366004613d2d565b611b7a565b5f54610412906001600160a01b031681565b600c54610412906001600160a01b031681565b610412610669366004614004565b611e5d565b6102cf61067c366004613d12565b611f03565b6102cf61068f366004614004565b611f7b565b6102e46106a2366004613d2d565b61213e565b6102e461216f565b6106b7601481565b60405160ff90911681526020016102ee565b6102cf6106d7366004613d12565b612181565b6004546106f89064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ee565b610757610724366004613d2d565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ee949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e460025481565b610797611623565b6001600160a01b0316336001600160a01b031614806107c057506001546001600160a01b031633145b6107dd57604051632864c4e160e01b815260040160405180910390fd5b6107e681611651565b819061080f576040516381e5828960e01b81526004016108069190613f3d565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff169061083d90600490836121bb565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161086a83614038565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a60205260408120600481015461090a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61091c61245d565b600c546001600160a01b0316156109455760405162035f5560e61b815260040160405180910390fd5b6001600160a01b03811661096c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d5416156109e357600d80546001600160a01b031981169091555f600e8190556040516001600160a01b039092169182917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a2505b6040516001600160a01b038216907f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b905f90a250565b5f888152600a602052604090206002815460ff166003811115610a3e57610a3e61404d565b14610a5c57604051634f4b461f60e11b815260040160405180910390fd5b600481015415610a7f5760405163632a22bb60e01b815260040160405180910390fd5b85610a9d57604051636caad1ed60e11b815260040160405180910390fd5b5f610b0182600601805480602002602001604051908101604052809291908181526020018280548015610af757602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610ad9575b505050505061248f565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610b53573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b7a91908101906141dd565b9050806101c0015115610b9757610b978b828a858b8b8b8b612595565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610bf5575f5ffd5b505af1158015610c07573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610c489695949392919061439f565b60405180910390a25050505050505050505050565b610c6561245d565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610cd657610cd6614061565b604051908082528060200260200182016040528015610cff578160200160208202803683370190505b509450806001600160401b03811115610d1a57610d1a614061565b604051908082528060200260200182016040528015610d43578160200160208202803683370190505b5093505f805b83811015610e3f575f856006018281548110610d6757610d676143ec565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610dad57610dad61404d565b03610e365780888481518110610dc557610dc56143ec565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610e1d57610e1d6143ec565b602090810291909101015282610e3281614400565b9350505b50600101610d49565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610e6d57610e6d61404d565b03610e8b57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610ec05760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610ee457610ee461404d565b14610f02576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610f49573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f6d9190614418565b905080610f806040860160208701614442565b63ffffffff161115610f986040860160208701614442565b829091610fc6576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610806565b5050815460ff1916600190811783558201859055436002830155600354610fed904261445b565b600383015561100160058301856002613c10565b5061100a61216f565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a1709261105b928a928a929161446e565b60405180910390a250600195945050505050565b5f61107982611651565b61108457505f919050565b6001546001600160a01b03166110ad576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906110dd908590600401613f3d565b602060405180830381865afa1580156110f8573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061111c91906144be565b92915050565b61112a61245d565b600d546001600160a01b0316806111545760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461119557604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610806565b50505f6202a300600e546111a9919061445b565b90508042818110156111d7576040516337c8270b60e01b815260048101929092526024820152604401610806565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690555f600e8190556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b9190a2505050565b5f818152600a602052604081206001815460ff1660038111156112585761125861404d565b1461126557505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156112ad576112ad61404d565b149392505050565b6112bd61245d565b6001600160a01b0381166112e45760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61133361245d565b61133c5f612791565b565b600b545f9081906001600160a01b0316331461136d5760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff1660038111156113925761139261404d565b146113b057604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff1660028111156113f0576113f061404d565b1461140057600b015491506114dd565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b820180549161143483614038565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611485929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6114ed611623565b6001600160a01b0316336001600160a01b0316148061151657506001546001600160a01b031633145b61153357604051632864c4e160e01b815260040160405180910390fd5b61153c81611651565b6116205760048054600160281b900464ffffffffff1690611566906001600160a01b038416612801565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916115b783614400565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016108cf565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906116a1576040516322e679e360e11b815260040160405180910390fd5b806006018054806020026020016040519081016040528092919081815260200182805480156116f757602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116116d9575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561173e5761173e61404d565b14159392505050565b61174f61245d565b6001600160a01b0381166117765760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f25906117ca906202a3009061445b565b60405190815260200160405180910390a250565b5f8181526009602052604090205460609081908190611810576040516322e679e360e11b815260040160405180910390fd5b5f848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561187557602002820191905f5260205f20905b815481526020019060010190808311611861575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156118c557602002820191905f5260205f20905b8154815260200190600101908083116118b1575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561191557602002820191905f5260205f20905b815481526020019060010190808311611901575b505050505090509250925092509193909250565b61193161245d565b6001600160a01b0381166119585760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6119a961245d565b600d546001600160a01b0316806119d35760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690555f600e8190556040516001600160a01b038316917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a250565b5f611a266129d7565b805490915060ff600160401b82041615906001600160401b03165f81158015611a4c5750825b90505f826001600160401b03166001148015611a675750303b155b905081158015611a75575080155b15611a935760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611abd57845460ff60401b1916600160401b1785555b6001600160a01b038716611ae45760405163d92e233d60e01b815260040160405180910390fd5b611aed336129ff565b611af960046014612a10565b611b0286610c5d565b611b0a611623565b6001600160a01b0316876001600160a01b031614611b2b57611b2b87612181565b8315611b7157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff166003811115611b9e57611b9e61404d565b03611bbc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611bd457611bd461404d565b14611bf257604051631860f69960e31b815260040160405180910390fd5b80600301544211611c1657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611cfb578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611cdc575f5ffd5b505af1158015611cee573d5f5f3e3d5ffd5b505f979650505050505050565b611d0482612a8f565b815460ff191660021782556006820154600b83018190555f816001600160401b03811115611d3457611d34614061565b604051908082528060200260200182016040528015611d5d578160200160208202803683370190505b5090505f5b82811015611dcf57846009015f866006018381548110611d8457611d846143ec565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611dbc57611dbc6143ec565b6020908102919091010152600101611d62565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611e12575f5ffd5b505af1158015611e24573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d7856006018360405161105b9291906144d7565b5f828152600a602052604081206002815460ff166003811115611e8257611e8261404d565b14611ea057604051634f4b461f60e11b815260040160405180910390fd5b60068101548390808210611ed0576040516326c5c55b60e11b815260048101929092526024820152604401610806565b5050806006018381548110611ee757611ee76143ec565b5f918252602090912001546001600160a01b0316949350505050565b611f0b61245d565b6001600160a01b038116611f325760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611f9f57611f9f61404d565b03611fbd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611fd557611fd561404d565b14611ff357604051631860f69960e31b815260040160405180910390fd5b806003015442111561201857604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff161561204a5760405163257309f160e11b815260040160405180910390fd5b6120533361106f565b6120705760405163149fbcfd60e11b815260040160405180910390fd5b61207b338385612bb5565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff191660011790559091506120fa90839083612d86565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f818152600960205260409020548061216a576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61217c60046014612f87565b905090565b61218961245d565b6001600160a01b0381166121b2575f604051631e4fbdf760e01b81526004016108069190613f3d565b61162081612791565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106121fa5760405162461bcd60e51b8152600401610806906144e9565b825464ffffffffff600160281b909104811690821681116122585760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610806565b825f5b81866001015f61226b8488613080565b64ffffffffff1681526020019081526020015f20819055505f8160016122919190614533565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116122c65750612455565b600185165f0361238d575f6122e5836122e088600161454c565b613080565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161234691600401614569565b602060405180830381865af4158015612361573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123859190614418565b935050612441565b5f61239d836122e0600189614599565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916123fe91600401614569565b602060405180830381865af4158015612419573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061243d9190614418565b9350505b50647fffffffff600194851c16930161225b565b505050505050565b33612466611623565b6001600160a01b03161461133c573360405163118cdaa760e01b81526004016108069190613f3d565b80515f908161249f8260146145b6565b6001600160401b038111156124b6576124b6614061565b6040519080825280601f01601f1916602001820160405280156124e0576020820181803683370190505b5090505f5b82811015612585575f858281518110612500576125006143ec565b602002602001015160601b90505f82601461251b91906145b6565b90505f5b60148110156125775782816014811061253a5761253a6143ec565b1a60f81b85612549838561445b565b81518110612559576125596143ec565b60200101906001600160f81b03191690815f1a90535060010161251f565b5050508060010190506124e5565b5080516020909101209392505050565b826125b357604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b81526004016125ea94939291906145cd565b602060405180830381865afa158015612605573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061262991906144be565b6126465760405163051d8aa760e51b815260040160405180910390fd5b8061266457604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661268d57604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b81526004016126dc97969594939291906145ec565b5f60405180830381865afa1580156126f6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261271d91908101906146c6565b5f8e8152600f6020908152604090912084519497509295509093506127459290860190613cb1565b505f8b8152601060209081526040909120835161276492850190613cb1565b505f8b8152601160209081526040909120825161278392840190613cb1565b505050505050505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106128505760405162461bcd60e51b8152600401610806906144e9565b825464ffffffffff908116908216106128a35760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610806565b6128ae81600161454c565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6128e58487613080565b64ffffffffff16815260208101919091526040015f205560018316156129d0575f612915826122e0600187614599565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161297691600401614569565b602060405180830381865af4158015612991573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129b59190614418565b647fffffffff600195861c16949093509190910190506128d5565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061111c565b612a0761309d565b611620816130c2565b602060ff82161115612a5e5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610806565b612a6f600160ff831681901b6147aa565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612bb0575f612aaa82600161445b565b90505b82811015612ba7575f846006018381548110612acb57612acb6143ec565b5f9182526020822001546006870180546001600160a01b0390921693509084908110612af957612af96143ec565b5f918252602090912001546001600160a01b0390811691508216811015612b9d5780866006018581548110612b3057612b306143ec565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612b7157612b716143ec565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612aad565b50600101612a96565b505050565b5f8211612bd55760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612bfe576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612c34916147aa565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612c7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c9f9190614418565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cf2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d169190614418565b90505f8111612d385760405163aeaddff160e01b815260040160405180910390fd5b5f612d4382846147bd565b90505f8111612d655760405163149fbcfd60e11b815260040160405180910390fd5b80861115611b715760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612e0457508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612f80565b5f5f90505f876009015f855f81548110612e2057612e206143ec565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612ea8575f896009015f878481548110612e6a57612e6a6143ec565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e9f578092508193505b50600101612e49565b50808610612ebc575f945050505050612f80565b5f88600a015f868581548110612ed457612ed46143ec565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612f1157612f1161404d565b021790555086848381548110612f2957612f296143ec565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612fda5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610806565b602060ff83161115612ffe5760405162461bcd60e51b8152600401610806906147dc565b8254600160281b900464ffffffffff168061301d60ff8516600261492d565b64ffffffffff16101561306d5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610806565b6130788482856130ca565b949350505050565b5f8161309360ff851663ffffffff614946565b612f80919061454c565b6130a5613192565b61133c57604051631afcd79f60e31b815260040160405180910390fd5b61218961309d565b5f602060ff831611156130ef5760405162461bcd60e51b8152600401610806906147dc565b8264ffffffffff165f0361310d57613106826131ab565b9050612f80565b5f613119836001614533565b60ff166001600160401b0381111561313357613133614061565b60405190808252806020026020018201604052801561315c578160200160208202803683370190505b50905061316b85858584613845565b808360ff1681518110613180576131806143ec565b60200260200101519150509392505050565b5f61319b6129d7565b54600160401b900460ff16919050565b5f8160ff165f036131bd57505f919050565b8160ff166001036131ef57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361322157507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361325357507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361328557507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036132b757507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036132e957507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361331b57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361334d57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361337f57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036133b157507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036133e357507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361341557507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361344757507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361347957507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036134ab57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036134dd57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361350f57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361354157507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361357357507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036135a557507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036135d757507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361360957507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361363b57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361366d57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361369f57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036136d157507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361370357507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361373557507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361376757507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361379957507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f036137cb57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036137fd57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610806565b602060ff831611156138695760405162461bcd60e51b8152600401610806906147dc565b5f8364ffffffffff16116138cd5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610806565b5f6138d9600185614599565b9050600181165f0361392c57846001015f6138f45f84613080565b64ffffffffff1681526020019081526020015f2054825f8151811061391b5761391b6143ec565b602002602001018181525050613954565b6139355f6131ab565b825f81518110613947576139476143ec565b6020026020010181815250505b5f5b8360ff168160ff16101561245557600182165f03613a4c5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff16815181106139a8576139a86143ec565b602002602001015181526020016139be856131ab565b8152506040518263ffffffff1660e01b81526004016139dd9190614569565b602060405180830381865af41580156139f8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613a1c9190614418565b83613a28836001614533565b60ff1681518110613a3b57613a3b6143ec565b602002602001018181525050613bfd565b5f613a58826001614533565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613afa575f876001015f613aaf856001613a9e9190614533565b60018864ffffffffff16901c613080565b64ffffffffff1681526020019081526020015f205490508085846001613ad59190614533565b60ff1681518110613ae857613ae86143ec565b60200260200101818152505050613bfb565b5f876001015f613b11856001886122e09190614599565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613b6857613b686143ec565b60200260200101518152506040518263ffffffff1660e01b8152600401613b8f9190614569565b602060405180830381865af4158015613baa573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bce9190614418565b85613bda856001614533565b60ff1681518110613bed57613bed6143ec565b602002602001018181525050505b505b647fffffffff600192831c169101613956565b600183019183908215613ca1579160200282015f5b83821115613c6f57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613c25565b8015613c9f5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613c6f565b505b50613cad929150613cea565b5090565b828054828255905f5260205f20908101928215613ca1579160200282015b82811115613ca1578251825591602001919060010190613ccf565b5b80821115613cad575f8155600101613ceb565b6001600160a01b0381168114611620575f5ffd5b5f60208284031215613d22575f5ffd5b8135612f8081613cfe565b5f60208284031215613d3d575f5ffd5b5035919050565b5f5f83601f840112613d54575f5ffd5b5081356001600160401b03811115613d6a575f5ffd5b602083019150836020828501011115613d81575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613d9f575f5ffd5b8835975060208901356001600160401b03811115613dbb575f5ffd5b613dc78b828c01613d44565b9098509650506040890135945060608901356001600160401b03811115613dec575f5ffd5b613df88b828c01613d44565b90955093505060808901356001600160401b03811115613e16575f5ffd5b613e228b828c01613d44565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613e6f5781516001600160a01b0316865260209586019590910190600101613e48565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613e6f578151865260209586019590910190600101613e8b565b604081525f613ebb6040830185613e36565b8281036020840152613ecd8185613e79565b95945050505050565b5f5f5f60808486031215613ee8575f5ffd5b833592506020840135915060808401851015613f02575f5ffd5b6040840190509250925092565b5f5f60408385031215613f20575f5ffd5b823591506020830135613f3281613cfe565b809150509250929050565b6001600160a01b0391909116815260200190565b5f5f5f60608486031215613f63575f5ffd5b833592506020840135613f7581613cfe565b929592945050506040919091013590565b602081525f612f806020830184613e36565b606081525f613faa6060830186613e79565b8281036020840152613fbc8186613e79565b90508281036040840152613fd08185613e79565b9695505050505050565b5f5f60408385031215613feb575f5ffd5b8235613ff681613cfe565b946020939093013593505050565b5f5f60408385031215614015575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161404657614046614024565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b038111828210171561409857614098614061565b60405290565b604051601f8201601f191681016001600160401b03811182821017156140c6576140c6614061565b604052919050565b80516004811061216a575f5ffd5b5f82601f8301126140eb575f5ffd5b604080519081016001600160401b038111828210171561410d5761410d614061565b8060405250806040840185811115614123575f5ffd5b845b8181101561413d578051835260209283019201614125565b509195945050505050565b805161216a81613cfe565b805160ff8116811461216a575f5ffd5b5f82601f830112614172575f5ffd5b81516001600160401b0381111561418b5761418b614061565b61419e601f8201601f191660200161409e565b8181528460208386010111156141b2575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b8051801515811461216a575f5ffd5b5f602082840312156141ed575f5ffd5b81516001600160401b03811115614202575f5ffd5b82016102008185031215614214575f5ffd5b61421c614075565b8151815261422c602083016140ce565b60208201526040828101519082015261424885606084016140dc565b606082015260a0820151608082015261426360c08301614148565b60a082015261427460e08301614153565b60c08201526101008201516001600160401b03811115614292575f5ffd5b61429e86828501614163565b60e0830152506142b16101208301614148565b6101008201526142c46101408301614148565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b038111156142fa575f5ffd5b61430686828501614163565b6101808301525061431a6101c08301614148565b6101a082015261432d6101e083016141ce565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613e6f5781546001600160a01b0316865260209095019460019182019101614350565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f6143b1608083018961433b565b82810360208401526143c481888a614377565b905085604084015282810360608401526143df818587614377565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161441157614411614024565b5060010190565b5f60208284031215614428575f5ffd5b5051919050565b803563ffffffff8116811461216a575f5ffd5b5f60208284031215614452575f5ffd5b612f808261442f565b8082018082111561111c5761111c614024565b84815260a0810160208201855f5b60028110156144a95763ffffffff6144938361442f565b168352602092830192919091019060010161447c565b50505060608201939093526080015292915050565b5f602082840312156144ce575f5ffd5b612f80826141ce565b604081525f613ebb604083018561433b565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff818116838216019081111561111c5761111c614024565b64ffffffffff818116838216019081111561111c5761111c614024565b6040810181835f5b6002811015614590578151835260209283019290910190600101614571565b50505092915050565b64ffffffffff828116828216039081111561111c5761111c614024565b808202811582820484141761111c5761111c614024565b848152836020820152606060408201525f613fd0606083018486614377565b60018060a01b038816815286602082015285604082015260a060608201525f61461960a083018688614377565b828103608084015261462c818587614377565b9a9950505050505050505050565b5f6001600160401b0382111561465257614652614061565b5060051b60200190565b5f82601f83011261466b575f5ffd5b815161467e6146798261463a565b61409e565b8082825260208201915060208360051b86010192508583111561469f575f5ffd5b602085015b838110156146bc5780518352602092830192016146a4565b5095945050505050565b5f5f5f606084860312156146d8575f5ffd5b83516001600160401b038111156146ed575f5ffd5b8401601f810186136146fd575f5ffd5b805161470b6146798261463a565b8082825260208201915060208360051b85010192508883111561472c575f5ffd5b6020840193505b8284101561474e578351825260209384019390910190614733565b8096505050505060208401516001600160401b0381111561476d575f5ffd5b6147798682870161465c565b92505060408401516001600160401b03811115614794575f5ffd5b6147a08682870161465c565b9150509250925092565b8181038181111561111c5761111c614024565b5f826147d757634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156114dd5780850481111561483e5761483e614024565b600184161561484c57908102905b60019390931c928002614823565b5f826148685750600161111c565b8161487457505f61111c565b816001811461488a5760028114614894576148c6565b600191505061111c565b60ff8411156148a5576148a5614024565b6001841b915064ffffffffff8211156148c0576148c0614024565b5061111c565b5060208310610133831016604e8410600b84101617156148fe575081810a64ffffffffff8111156148f9576148f9614024565b61111c565b61490e64ffffffffff848461481f565b8064ffffffffff0482111561492557614925614024565b029392505050565b5f612f8064ffffffffff841664ffffffffff841661485a565b64ffffffffff818116838216029081169081811461496657614966614024565b509291505056fea164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 9049 + "start": 9206 }, { "length": 20, - "start": 9233 + "start": 9390 }, { "length": 20, - "start": 10633 + "start": 10790 }, { "length": 20, - "start": 14761 + "start": 14918 }, { "length": 20, - "start": 15203 + "start": 15360 } ] } @@ -1629,28 +1629,28 @@ "PoseidonT3": [ { "length": 20, - "start": 8835 + "start": 8992 }, { "length": 20, - "start": 9019 + "start": 9176 }, { "length": 20, - "start": 10419 + "start": 10576 }, { "length": 20, - "start": 14547 + "start": 14704 }, { "length": 20, - "start": 14989 + "start": 15146 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-da8d0a9494cb27300af0bf31d68e29d6a69f32c3" + "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index 4f6708283..0b69e846b 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -515,10 +515,12 @@ interface ICiphernodeRegistry { /// be published yet — it works as soon as the committee is finalized, /// which is what `publishCommittee` callers need in order to bind a /// `partyId` to a specific operator before the public key is stored. + /// Reverts `CommitteeNotFinalized` for non-finalized committees so the + /// provisional, sortition-in-progress `topNodes` is never exposed. /// @param e3Id ID of the E3 computation /// @param partyId Index into `topNodes` /// @return Operator address at `topNodes[partyId]` - function getCommitteeNodeAt( + function canonicalCommitteeNodeAt( uint256 e3Id, uint256 partyId ) external view returns (address); diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 4321d28c7..29c9a2073 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -360,6 +360,14 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { ); require(address(verifier) != address(0), ZeroAddress()); dkgFoldAttestationVerifier = verifier; + // Invalidate any stale pending proposal made before the initial set, + // so it cannot later be committed and silently bypass the timelock. + if (pendingDkgFoldAttestationVerifier != address(0)) { + address staleProposal = pendingDkgFoldAttestationVerifier; + pendingDkgFoldAttestationVerifier = address(0); + pendingDkgFoldAttestationVerifierAt = 0; + emit DkgFoldAttestationVerifierProposalCancelled(staleProposal); + } emit DkgFoldAttestationVerifierUpdated(address(verifier)); } @@ -716,13 +724,23 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { } /// @inheritdoc ICiphernodeRegistry - function getCommitteeNodeAt( + function canonicalCommitteeNodeAt( uint256 e3Id, uint256 partyId ) external view returns (address) { - address[] storage top = committees[e3Id].topNodes; - require(partyId < top.length, PartyIdOutOfBounds(partyId, top.length)); - return top[partyId]; + Committee storage c = committees[e3Id]; + // Only expose `partyId -> node` for canonical (finalized) committees. + // Pre-finalization, `topNodes` is still being populated by sortition + // and is not the canonical mapping. + require( + c.stage == ICiphernodeRegistry.CommitteeStage.Finalized, + CommitteeNotFinalized() + ); + require( + partyId < c.topNodes.length, + PartyIdOutOfBounds(partyId, c.topNodes.length) + ); + return c.topNodes[partyId]; } /// @inheritdoc ICiphernodeRegistry diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index b6f40b927..51a0bd6ef 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -178,7 +178,7 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { return false; } - function getCommitteeNodeAt( + function canonicalCommitteeNodeAt( uint256 e3Id, uint256 partyId ) external view returns (address) { @@ -331,7 +331,7 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { return false; } - function getCommitteeNodeAt( + function canonicalCommitteeNodeAt( uint256, uint256 ) external pure returns (address) { diff --git a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol index 0b629fb42..4ff4a6355 100644 --- a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol @@ -144,15 +144,17 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { BundleData memory data, DkgFoldAttestationLib.PartySlotBinding memory binding ) private view returns (uint256 slot, bytes32 skCommit, bytes32 esmCommit) { - // Structural check: the binding's `node` must be the operator - // registered at `topNodes[partyId]` on chain. This prevents an - // aggregator from reassigning a node's signed attestation to a - // different slot (e.g. claiming the operator at `topNodes[0]` is - // party 1) even if the operator cooperated by signing with the - // wrong `partyId`. Combined with the `ecrecover` check below, the - // attestation is bound to *both* the right address and the right slot. + // Canonical-slot binding: `binding.node` must equal the canonical + // operator at index `partyId` of the finalized committee — i.e. + // `canonicalCommitteeNodeAt(e3Id, partyId)`, which returns + // `topNodes[partyId]` in address-ascending order. `partyId` is the + // canonical sortition slot id, so this rejects any binding pointing + // to an operator who is not the canonical occupant of that slot, + // even one who is otherwise an active committee member. Combined + // with the EIP-712 `ecrecover` check below, the attestation is + // bound to *both* the right address and the right canonical slot. require( - ICiphernodeRegistry(registry).getCommitteeNodeAt( + ICiphernodeRegistry(registry).canonicalCommitteeNodeAt( e3Id, binding.partyId ) == binding.node, diff --git a/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts b/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts index e7f3b9532..61af98c3f 100644 --- a/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts +++ b/packages/enclave-contracts/scripts/deployAndSave/dkgFoldAttestationVerifier.ts @@ -9,16 +9,20 @@ import { DkgFoldAttestationVerifier, DkgFoldAttestationVerifier__factory as DkgFoldAttestationVerifierFactory, } from "../../types"; -import { readDeploymentArgs, storeDeploymentArgs } from "../utils"; +import { + getDeploymentChain, + readDeploymentArgs, + storeDeploymentArgs, +} from "../utils"; export const deployAndSaveDkgFoldAttestationVerifier = async ( hre: HardhatRuntimeEnvironment, ): Promise<{ dkgFoldAttestationVerifier: DkgFoldAttestationVerifier; }> => { - const { ethers, networkName } = await hre.network.connect(); + const { ethers } = await hre.network.connect(); const [signer] = await ethers.getSigners(); - const chain = networkName ?? "localhost"; + const chain = getDeploymentChain(hre); const existing = readDeploymentArgs("DkgFoldAttestationVerifier", chain); if (existing?.address) { diff --git a/packages/enclave-contracts/tasks/ciphernode.ts b/packages/enclave-contracts/tasks/ciphernode.ts index 736ac5e26..f624f8fa9 100644 --- a/packages/enclave-contracts/tasks/ciphernode.ts +++ b/packages/enclave-contracts/tasks/ciphernode.ts @@ -7,6 +7,45 @@ import { ZeroAddress } from "ethers"; import { task } from "hardhat/config"; import { ArgumentType } from "hardhat/types/arguments"; +/** + * Resolve the right impersonation RPC prefix for the connected provider. + * Anvil exposes `anvil_*`; Hardhat's in-process network exposes `hardhat_*`. + * Probes once via `anvil_impersonateAccount` and falls back to `hardhat_*`. + * Throws a clear error if neither is supported (e.g. a real RPC). + */ +async function resolveImpersonationRpc( + provider: { send: (method: string, params: unknown[]) => Promise }, + probeAddress: string, +): Promise<{ + impersonate: string; + setBalance: string; + stopImpersonating: string; +}> { + try { + await provider.send("anvil_impersonateAccount", [probeAddress]); + await provider.send("anvil_stopImpersonatingAccount", [probeAddress]); + return { + impersonate: "anvil_impersonateAccount", + setBalance: "anvil_setBalance", + stopImpersonating: "anvil_stopImpersonatingAccount", + }; + } catch { + try { + await provider.send("hardhat_impersonateAccount", [probeAddress]); + await provider.send("hardhat_stopImpersonatingAccount", [probeAddress]); + return { + impersonate: "hardhat_impersonateAccount", + setBalance: "hardhat_setBalance", + stopImpersonating: "hardhat_stopImpersonatingAccount", + }; + } catch (err) { + throw new Error( + "Provider does not support account impersonation. Run this task against an Anvil or Hardhat local node (e.g. `--network localhost` with `anvil` or `npx hardhat node`).", + ); + } + } +} + export const ciphernodeAdd = task( "ciphernode:add", "Register a ciphernode to the bonding registry and ciphernode registry", @@ -280,7 +319,7 @@ export const ciphernodeMintTokens = task( export const ciphernodeAdminAdd = task( "ciphernode:admin-add", - "Register a ciphernode using admin privileges (for testing/setup)", + "Register a ciphernode using admin privileges (for testing/setup). Requires a local node that supports account impersonation (Anvil or Hardhat).", ) .addOption({ name: "ciphernodeAddress", @@ -401,8 +440,12 @@ export const ciphernodeAdminAdd = task( console.log( "Step 3: Impersonating ciphernode for license operations...", ); - await provider.send("anvil_impersonateAccount", [ciphernodeAddress]); - await provider.send("anvil_setBalance", [ + const impersonationRpc = await resolveImpersonationRpc( + provider, + ciphernodeAddress, + ); + await provider.send(impersonationRpc.impersonate, [ciphernodeAddress]); + await provider.send(impersonationRpc.setBalance, [ ciphernodeAddress, "0x1000000000000000000000", ]); @@ -429,7 +472,7 @@ export const ciphernodeAdminAdd = task( "Operator registered (automatically added to CiphernodeRegistry)", ); - await provider.send("anvil_stopImpersonatingAccount", [ + await provider.send(impersonationRpc.stopImpersonating, [ ciphernodeAddress, ]); @@ -441,8 +484,8 @@ export const ciphernodeAdminAdd = task( ); await approveUsdcTx.wait(); - await provider.send("anvil_impersonateAccount", [ciphernodeAddress]); - await provider.send("anvil_setBalance", [ + await provider.send(impersonationRpc.impersonate, [ciphernodeAddress]); + await provider.send(impersonationRpc.setBalance, [ ciphernodeAddress, "0x1000000000000000000000", ]); @@ -469,7 +512,7 @@ export const ciphernodeAdminAdd = task( await addTicketTx.wait(); console.log(`Ticket balance added: ${ticketAmount} USDC worth`); - await provider.send("anvil_stopImpersonatingAccount", [ + await provider.send(impersonationRpc.stopImpersonating, [ ciphernodeAddress, ]); diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 1431f6fb3..9a2e43a3e 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -13,7 +13,7 @@ chains: deploy_block: 13 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 12 + deploy_block: 11 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" deploy_block: 6 diff --git a/templates/default/scripts/anvil-automine.mjs b/templates/default/scripts/anvil-automine.mjs index 28f050f55..36eb9e774 100644 --- a/templates/default/scripts/anvil-automine.mjs +++ b/templates/default/scripts/anvil-automine.mjs @@ -18,12 +18,22 @@ async function rpc(method, params = []) { } } +let failureCount = 0 +let lastLoggedTime = 0 +const LOG_INTERVAL_MS = 30_000 + async function loop() { for (;;) { try { await rpc('evm_mine') - } catch { - // anvil not up yet + failureCount = 0 + } catch (err) { + failureCount++ + const now = Date.now() + if (failureCount === 1 || now - lastLoggedTime >= LOG_INTERVAL_MS) { + console.error(`[anvil-automine] evm_mine failed (attempt ${failureCount}):`, err) + lastLoggedTime = now + } } await new Promise((r) => setTimeout(r, 1000)) } diff --git a/tests/integration/base.sh b/tests/integration/base.sh index 59b7fe685..526174b92 100755 --- a/tests/integration/base.sh +++ b/tests/integration/base.sh @@ -103,7 +103,7 @@ if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then pnpm e3-program:setMockEnclave --network localhost heading "Encrypt plaintext under the published committee pubkey" - $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" + $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext "$PLAINTEXT" --params "$ENCODED_PARAMS" waiton "$SCRIPT_DIR/output/output.bin" heading "Publish E3 input (forwards to publishCiphertextOutput; nodes run decryption with ZK proofs)" @@ -113,7 +113,7 @@ if [[ "$PROOF_AGGREGATION_ENABLED" == "true" ]]; then wait_for_plaintext_output 0 "$SCRIPT_DIR/output/plaintext.txt" "$INTEGRATION_DKG_TIMEOUT" else heading "Mock encrypted plaintext" - $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" + $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext "$PLAINTEXT" --params "$ENCODED_PARAMS" heading "Mock publish input e3-id" pnpm e3-program:publishInput --network localhost --e3-id 0 --data 0x12345678 diff --git a/tests/integration/persist.sh b/tests/integration/persist.sh index 7e1de7ec0..791b8a838 100755 --- a/tests/integration/persist.sh +++ b/tests/integration/persist.sh @@ -100,7 +100,7 @@ enclave_nodes_start "$ACTIVE_AGG" sleep 5 heading "Mock encrypted plaintext" -$SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" +$SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext "$PLAINTEXT" --params "$ENCODED_PARAMS" heading "Mock publish input e3-id" pnpm e3-program:publishInput --network localhost --e3-id 0 --data 0x12345678 From 0e9825e689ad40bf15391fbe4da2a7b07e5ecea1 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 11:43:54 +0200 Subject: [PATCH 30/54] remove unused err --- packages/enclave-contracts/tasks/ciphernode.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/enclave-contracts/tasks/ciphernode.ts b/packages/enclave-contracts/tasks/ciphernode.ts index f624f8fa9..04fe65ce2 100644 --- a/packages/enclave-contracts/tasks/ciphernode.ts +++ b/packages/enclave-contracts/tasks/ciphernode.ts @@ -38,9 +38,9 @@ async function resolveImpersonationRpc( setBalance: "hardhat_setBalance", stopImpersonating: "hardhat_stopImpersonatingAccount", }; - } catch (err) { + } catch { throw new Error( - "Provider does not support account impersonation. Run this task against an Anvil or Hardhat local node (e.g. `--network localhost` with `anvil` or `npx hardhat node`).", + "Provider does not support account impersonation. Run this task against an Anvil or Hardhat local node (e.g. `--network localhost` with `anvil` or `npx hardhat node`)", ); } } From f4cce353bc4f70cd6e3f82e4091a3fee89b67286 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 15:21:58 +0200 Subject: [PATCH 31/54] fix crisp --- crates/evm/src/slashing_manager_sol_writer.rs | 12 +- .../src/actors/accusation_manager.rs | 38 ++++++ .../actors/commitment_consistency_checker.rs | 6 + .../src/components/Cards/PollCardResult.tsx | 6 +- examples/CRISP/client/src/utils/methods.ts | 4 +- examples/CRISP/enclave.config.yaml | 17 ++- examples/CRISP/package.json | 3 + .../packages/crisp-contracts/deploy/deploy.ts | 17 ++- .../crisp-contracts/deployed_contracts.json | 62 ++++++--- examples/CRISP/server/.env.example | 9 +- examples/CRISP/server/src/cli/commands.rs | 120 ++++++++++++++++-- examples/CRISP/server/src/cli/main.rs | 11 +- examples/CRISP/server/src/config.rs | 15 +++ examples/CRISP/server/src/deployments.rs | 70 ++++++++++ examples/CRISP/server/src/lib.rs | 1 + examples/CRISP/server/src/server/indexer.rs | 32 ++++- .../CRISP/server/src/server/routes/voting.rs | 19 ++- packages/enclave-contracts/package.json | 1 + .../scripts/configureLocalSlashingPolicies.ts | 101 +++++++++++++++ .../scripts/deployEnclave.ts | 6 + .../scripts/runConfigureSlashingPolicies.ts | 13 ++ packages/enclave-contracts/scripts/utils.ts | 35 ++++- 22 files changed, 521 insertions(+), 77 deletions(-) create mode 100644 examples/CRISP/server/src/deployments.rs create mode 100644 packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts create mode 100644 packages/enclave-contracts/scripts/runConfigureSlashingPolicies.ts diff --git a/crates/evm/src/slashing_manager_sol_writer.rs b/crates/evm/src/slashing_manager_sol_writer.rs index c3796ce6b..a709407f6 100644 --- a/crates/evm/src/slashing_manager_sol_writer.rs +++ b/crates/evm/src/slashing_manager_sol_writer.rs @@ -160,13 +160,15 @@ impl Handler { let decoded = format_evm_error(&err); - if rank > 0 { + let benign = decoded.contains("OperatorNotInCommittee") + || decoded.contains("VoterNotInCommittee") + || decoded.contains("DuplicateEvidence"); + if rank > 0 || benign { // Fallback submitters expect DuplicateEvidence reverts // when the primary submitter has already landed the tx. - warn!( - "Fallback submitter (rank {rank}): slash submission failed \ - (likely already submitted by primary): {decoded}" - ); + // Operator/VoterNotInCommittee indicate a stale off-chain accusation + // (e.g. cross-E3 race) — not a node-local fault. + warn!("Slash submission skipped (rank {rank}): {decoded}"); } else { bus.err( EType::Evm, diff --git a/crates/zk-prover/src/actors/accusation_manager.rs b/crates/zk-prover/src/actors/accusation_manager.rs index 87d142a59..eb4b12366 100644 --- a/crates/zk-prover/src/actors/accusation_manager.rs +++ b/crates/zk-prover/src/actors/accusation_manager.rs @@ -340,6 +340,10 @@ impl AccusationManager { ec: &EventContext, ctx: &mut Context, ) { + if event.e3_id != self.e3_id { + return; + } + let accused_address = if event.accused_address == Address::ZERO { if let Some(&addr) = self.committee.get(event.accused_party_id as usize) { warn!( @@ -358,6 +362,14 @@ impl AccusationManager { event.accused_address }; + if !self.committee.contains(&accused_address) { + warn!( + "Ignoring proof failure for {} — not on E3 {} committee", + accused_address, self.e3_id + ); + return; + } + // Cache the failed verification result. // Evidence preimage = `abi.encode(proof.data, public_signals)` — matches // the on-chain `keccak256(evidence) == dataHash` check in SlashingManager. @@ -407,6 +419,18 @@ impl AccusationManager { ec: &EventContext, ctx: &mut Context, ) { + if data.e3_id != self.e3_id { + return; + } + + if !self.committee.contains(&data.accused_address) { + warn!( + "Ignoring commitment violation for {} — not on E3 {} committee", + data.accused_address, self.e3_id + ); + return; + } + // Cache as a failed verification for voting on future accusations. // `data.evidence` carries the raw `abi.encode(proof.data, public_signals)` // preimage of `data_hash`, populated by the consistency checker. Slashing @@ -448,6 +472,14 @@ impl AccusationManager { ec: &EventContext, ctx: &mut Context, ) { + if !self.committee.contains(&accused_address) { + warn!( + "Refusing accusation against {} — not on E3 {} committee", + accused_address, self.e3_id + ); + return; + } + let key = (accused_address, proof_type); // Dedup: don't create multiple accusations for the same (accused, proof_type) @@ -1264,6 +1296,12 @@ impl Handler> for AccusationManager { _ctx: &mut Self::Context, ) -> Self::Result { let (data, _ec) = msg.into_components(); + if data.e3_id != self.e3_id { + return; + } + if !self.committee.contains(&data.address) { + return; + } // Cache successful verification for voting on future accusations. // Evidence preimage = `abi.encode(proof.data, public_signals)`. let evidence: Bytes = ( diff --git a/crates/zk-prover/src/actors/commitment_consistency_checker.rs b/crates/zk-prover/src/actors/commitment_consistency_checker.rs index 872a35a50..73a707acd 100644 --- a/crates/zk-prover/src/actors/commitment_consistency_checker.rs +++ b/crates/zk-prover/src/actors/commitment_consistency_checker.rs @@ -363,6 +363,9 @@ impl Handler> for CommitmentConsistencyCheck _ctx: &mut Self::Context, ) -> Self::Result { let (data, ec) = msg.into_components(); + if data.e3_id != self.e3_id { + return; + } let proof_type = data.proof_type; let address = data.address; @@ -392,6 +395,9 @@ impl Handler> for CommitmentCons _ctx: &mut Self::Context, ) -> Self::Result { let (data, ec) = msg.into_components(); + if data.e3_id != self.e3_id { + return; + } let mut inconsistent_parties = BTreeSet::new(); diff --git a/examples/CRISP/client/src/components/Cards/PollCardResult.tsx b/examples/CRISP/client/src/components/Cards/PollCardResult.tsx index 1acc4dfda..3cbe3bf84 100644 --- a/examples/CRISP/client/src/components/Cards/PollCardResult.tsx +++ b/examples/CRISP/client/src/components/Cards/PollCardResult.tsx @@ -20,9 +20,13 @@ type PollCardResultProps = { } const PollCardResult: React.FC = ({ isResult, results, totalVotes, isActive }) => { const validVotes = results.reduce((sum, poll) => sum + Number.parseInt(poll.votes.toString(), 10), 0) + const percentDenominator = validVotes > 0 ? validVotes : totalVotes const calculatePercentage = (votes: number) => { - return ((votes / validVotes) * 100).toFixed(0) + if (percentDenominator <= 0) { + return '0' + } + return ((votes / percentDenominator) * 100).toFixed(0) } return ( diff --git a/examples/CRISP/client/src/utils/methods.ts b/examples/CRISP/client/src/utils/methods.ts index 69e03ee3a..8184d1fe3 100644 --- a/examples/CRISP/client/src/utils/methods.ts +++ b/examples/CRISP/client/src/utils/methods.ts @@ -50,13 +50,13 @@ export const convertPollData = (request: PollRequestResult[]): PollResult[] => { const options: PollOption[] = [ { value: 0, - votes: poll.tally[0] ?? 0, + votes: Number.parseInt(poll.tally[0] ?? '0', 10) || 0, label: poll.option_1_emoji, checked: false, }, { value: 1, - votes: poll.tally[1] ?? 0, + votes: Number.parseInt(poll.tally[1] ?? '0', 10) || 0, label: poll.option_2_emoji, checked: false, }, diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index 7757b9f9e..3baece8be 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -5,23 +5,26 @@ chains: rpc_url: ws://localhost:8545 contracts: e3_program: - address: "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" - deploy_block: 37 + address: "0x1429859428C0aBc9C2C47C8Ee9FBaf82cFA0F20f" + deploy_block: 43 enclave: address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" - deploy_block: 17 + deploy_block: 13 ciphernode_registry: address: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" - deploy_block: 13 + deploy_block: 9 bonding_registry: address: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" - deploy_block: 14 + deploy_block: 10 slashing_manager: address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" - deploy_block: 12 + deploy_block: 8 fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" - deploy_block: 8 + deploy_block: 4 + dkg_fold_attestation_verifier: + address: "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0" + deploy_block: 40 - name: "sepolia" enabled: false # Public Sepolia WebSocket endpoint (see repo docs for the recommended default). diff --git a/examples/CRISP/package.json b/examples/CRISP/package.json index 309e70ee5..b1916a736 100644 --- a/examples/CRISP/package.json +++ b/examples/CRISP/package.json @@ -20,6 +20,9 @@ "ciphernode:mint:tokens": "pnpm -C packages/crisp-contracts ciphernode:mint:tokens", "ciphernode:add:self": "pnpm -C packages/crisp-contracts ciphernode:add:self", "deploy:contracts": "pnpm -C packages/crisp-contracts deploy:contracts", + "deploy:contracts:mock": "pnpm -C packages/crisp-contracts deploy:contracts:mock", + "deploy:contracts:full": "pnpm -C packages/crisp-contracts deploy:contracts:full", + "deploy:contracts:full:mock": "pnpm -C packages/crisp-contracts deploy:contracts:full:mock", "test": "pnpm test:e2e", "test:circuits": "cd circuits/lib && nargo test", "compile:circuits": "bash ./scripts/compile_circuits.sh", diff --git a/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts b/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts index 57f0711fd..29c215a97 100644 --- a/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts +++ b/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts @@ -18,6 +18,7 @@ const contractMapping: Record = { BondingRegistry: 'bonding_registry', SlashingManager: 'slashing_manager', MockUSDC: 'fee_token', + DkgFoldAttestationVerifier: 'dkg_fold_attestation_verifier', } // Get __dirname equivalent in ES modules @@ -45,18 +46,26 @@ export const deploy = async () => { if (shouldPrintEnv) { const enclaveAddress = readDeploymentArgs('Enclave', chain)?.address - const tokenAddress = readDeploymentArgs('MockUSDC', chain)?.address + const feeTokenAddress = readDeploymentArgs('MockUSDC', chain)?.address const programAddress = readDeploymentArgs('CRISPProgram', chain)?.address const ciphernodeRegistryAddress = readDeploymentArgs('CiphernodeRegistryOwnable', chain)?.address + const votingTokenAddress = readDeploymentArgs('MockVotingToken', chain)?.address - if (!enclaveAddress || !tokenAddress || !programAddress || !ciphernodeRegistryAddress) { + if (!enclaveAddress || !feeTokenAddress || !programAddress || !ciphernodeRegistryAddress || !votingTokenAddress) { console.error('Error: Missing deployment addresses. Ensure all contracts are deployed.') return } - console.log('\nAdd these to your server .env') + console.log('\nAdd these to examples/CRISP/server/.env (and client/.env for VITE_CRISP_TOKEN):') console.log( - `ENCLAVE_ADDRESS=${enclaveAddress}\nFEE_TOKEN_ADDRESS=${tokenAddress}\nE3_PROGRAM_ADDRESS=${programAddress}\nCIPHERNODE_REGISTRY_ADDRESS=${ciphernodeRegistryAddress}`, + [ + `ENCLAVE_ADDRESS=${enclaveAddress}`, + `FEE_TOKEN_ADDRESS=${feeTokenAddress}`, + `E3_PROGRAM_ADDRESS=${programAddress}`, + `CIPHERNODE_REGISTRY_ADDRESS=${ciphernodeRegistryAddress}`, + `CRISP_VOTING_TOKEN=${votingTokenAddress}`, + `VITE_CRISP_TOKEN=${votingTokenAddress}`, + ].join('\n'), ) } } diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index aa9b9976f..9a321ede9 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -255,36 +255,66 @@ "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, "MockComputeProvider": { - "blockNumber": 17, - "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" + "blockNumber": 28, + "address": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf" }, "MockDecryptionVerifier": { - "blockNumber": 18, - "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" + "blockNumber": 29, + "address": "0x9d4454B023096f34B160D6B654540c56A1F81688" }, "MockPkVerifier": { - "blockNumber": 19, - "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" + "blockNumber": 30, + "address": "0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00" }, "MockE3Program": { - "blockNumber": 20, - "address": "0x851356ae760d987E095750cCeb3bC6014560891C" + "blockNumber": 31, + "address": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570" }, "ZKTranscriptLib": { - "blockNumber": 22, - "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" + "blockNumber": 33, + "address": "0x4c5859f0F772848b2D91F1D83E2Fe57935348029" + }, + "DecryptionAggregatorVerifier": { + "blockNumber": 34, + "address": "0x1291Be112d480055DaFd8a610b7d1e203891C274" + }, + "DkgAggregatorVerifier": { + "blockNumber": 35, + "address": "0x5f3f1dBD7B74C6B46e8c44f98792A1dAf8d69154" + }, + "BfvDecryptionVerifier": { + "blockNumber": 36, + "address": "0xb7278A61aa25c888815aFC32Ad3cC52fF24fE575" + }, + "BfvPkVerifier": { + "blockNumber": 38, + "address": "0x82e01223d51Eb87e16A03E24687EDF0F294da6f1" + }, + "DkgFoldAttestationVerifier": { + "address": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "blockNumber": 40 + }, + "MockRISC0Verifier": { + "address": "0xc351628EB244ec633d5f21fBD6621e1a683B1181", + "blockNumber": 42 }, "HonkVerifier": { - "blockNumber": 23, - "address": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf" + "address": "0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc", + "blockNumber": 43 }, "CRISPProgram": { - "blockNumber": 25, - "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" + "address": "0x1429859428C0aBc9C2C47C8Ee9FBaf82cFA0F20f", + "blockNumber": 43, + "constructorArgs": { + "enclave": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + "verifierAddress": "0xc351628EB244ec633d5f21fBD6621e1a683B1181", + "honkVerifierAddress": "0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc", + "imageId": "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2" + } }, "MockVotingToken": { - "blockNumber": 26, - "address": "0x9d4454B023096f34B160D6B654540c56A1F81688" + "address": "0x162A433068F51e18b7d13932F27e66a3f99E6890", + "blockNumber": 45 } } } \ No newline at end of file diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index 407f82e11..a1c29dd19 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -1,6 +1,7 @@ # Private key for the enclave server PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -ENCLAVE_SERVER_URL=http://0.0.0.0:4000 +# Use 127.0.0.1 (not 0.0.0.0) — program-server webhooks and CLI connect outbound to this URL. +ENCLAVE_SERVER_URL=http://127.0.0.1:4000 HTTP_RPC_URL=http://127.0.0.1:8545 PROGRAM_SERVER_URL=http://127.0.0.1:13151 WS_RPC_URL=ws://127.0.0.1:8545 @@ -12,12 +13,14 @@ ETHERSCAN_API_KEY="" # Cron-job API key to trigger new rounds CRON_API_KEY=1234567890 -# Enclave stack: sync from packages/enclave-contracts/deployed_contracts.json (localhost) after pnpm evm:deploy. -# E3_PROGRAM_ADDRESS (CRISPProgram): set by `cd packages/crisp-contracts && USE_MOCKS=true PRINT_ENV_VARS=true pnpm deploy:contracts` +# Enclave stack: sync from deploy output after each `pnpm dev:up` (see crisp_deploy / PRINT_ENV_VARS). +# Stale E3_PROGRAM_ADDRESS causes requestE3 to revert with empty data `0x`. ENCLAVE_ADDRESS=0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e FEE_TOKEN_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 E3_PROGRAM_ADDRESS=0x0E801D84Fa97b50751Dbf25036d067dCf18858bF CIPHERNODE_REGISTRY_ADDRESS=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 +# CRISP voting eligibility token (MockVotingToken) — NOT the fee token above +CRISP_VOTING_TOKEN= # Mock contracts registered on Enclave during deploy (reference only) MOCK_COMPUTE_PROVIDER_ADDRESS=0x9E545E3C0baAB3E08CdfD552C960A1050f373042 diff --git a/examples/CRISP/server/src/cli/commands.rs b/examples/CRISP/server/src/cli/commands.rs index e0dfdb16d..3d7bad7f2 100644 --- a/examples/CRISP/server/src/cli/commands.rs +++ b/examples/CRISP/server/src/cli/commands.rs @@ -17,7 +17,9 @@ use super::CLI_DB; use alloy::primitives::{Address, Bytes, U256}; use alloy::providers::{Provider, ProviderBuilder}; use alloy::sol_types::SolValue; +use anyhow::anyhow; use crisp::config::CONFIG; +use crisp::deployments; use e3_fhe_params::build_bfv_params_from_set_arc; use e3_sdk::evm_helpers::contracts::{CommitteeSize, EnclaveContract, EnclaveRead, EnclaveWrite}; use fhe::bfv::{BfvParameters, Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; @@ -54,6 +56,90 @@ struct CTRequest { ct_bytes: Vec, } +/// Seconds between `block.timestamp` and `inputWindow[0]` (covers approve + enable txs on Anvil). +const INPUT_WINDOW_START_BUFFER_SECS: u64 = 60; + +const ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000"; + +/// `InsufficientCiphernodes(uint256,uint256)` on CiphernodeRegistry. +const INSUFFICIENT_CIPHERNODES_SELECTOR: &str = "0x44ec930f"; + +fn format_request_e3_revert(err: impl std::fmt::Display) -> anyhow::Error { + let msg = err.to_string(); + if msg.contains(INSUFFICIENT_CIPHERNODES_SELECTOR) { + return anyhow!( + "request_e3 reverted: InsufficientCiphernodes — the committee size needs N active \ + operators (Micro N=3) but bondingRegistry.numActiveOperators() is 0. Register \ + ciphernodes before init: run full `pnpm dev:up`, or from examples/CRISP run \ + `pnpm ciphernode:add --ciphernode-address --network localhost` for at least \ + three addresses in enclave.config.yaml (cn1–cn3)." + ); + } + anyhow!( + "request_e3 reverted: {msg}. Common causes: stale E3_PROGRAM_ADDRESS in server/.env \ + (must match deployed CRISPProgram), inputWindow start in the past, or no registered \ + ciphernodes on the chain." + ) +} + +pub fn default_voting_token_hint() -> String { + deployments::localhost_mock_voting_token() + .ok() + .flatten() + .unwrap_or_else(|| ZERO_ADDRESS.to_string()) +} + +fn resolve_voting_token( + token_address: &str, +) -> Result> { + let trimmed = token_address.trim(); + if !trimmed.is_empty() && !trimmed.eq_ignore_ascii_case(ZERO_ADDRESS) { + return Ok(trimmed.to_string()); + } + if let Some(ref configured) = CONFIG.crisp_voting_token { + let configured = configured.trim(); + if !configured.is_empty() && !configured.eq_ignore_ascii_case(ZERO_ADDRESS) { + return Ok(configured.to_string()); + } + } + if let Some(addr) = deployments::localhost_mock_voting_token()? { + info!("Using MockVotingToken from deployed_contracts.json: {addr}"); + return Ok(addr); + } + Err(anyhow!( + "Voting token address is unset. After `pnpm dev:up`, copy `CRISP_VOTING_TOKEN` from deploy \ + output into server/.env, or pass `--token-address `." + ) + .into()) +} + +async fn ensure_e3_program_deployed( + e3_program: Address, +) -> Result<(), Box> { + if let Some(deployed) = deployments::localhost_crisp_program()? { + let deployed_addr: Address = deployed.parse()?; + if deployed_addr != e3_program { + return Err(anyhow!( + "E3_PROGRAM_ADDRESS in server/.env ({e3_program}) does not match deployed \ + CRISPProgram ({deployed_addr}). Re-run `pnpm dev:up` and update server/.env from \ + deploy output (PRINT_ENV_VARS=true)." + ) + .into()); + } + } + + let provider = ProviderBuilder::new().connect(&CONFIG.http_rpc_url).await?; + let code = provider.get_code_at(e3_program).await?; + if code.is_empty() { + return Err(anyhow!( + "No contract bytecode at E3_PROGRAM_ADDRESS {e3_program}. Stale server/.env after \ + redeploy is the usual cause — sync from packages/crisp-contracts/deployed_contracts.json." + ) + .into()); + } + Ok(()) +} + pub async fn get_current_timestamp() -> Result> { let provider = ProviderBuilder::new().connect(&CONFIG.http_rpc_url).await?; let block = provider @@ -79,11 +165,6 @@ pub async fn initialize_crisp_round( token_address: &str, balance_threshold: &str, ) -> Result> { - info!( - "Starting new CRISP round with token address: {} and balance threshold: {}", - token_address, balance_threshold - ); - let contract = EnclaveContract::new( &CONFIG.http_rpc_url, &CONFIG.private_key, @@ -109,7 +190,15 @@ pub async fn initialize_crisp_round( Err(e) => info!("Error checking E3 Program enabled: {:?}", e), } - let token_address: Address = token_address.parse()?; + let token_address_str = resolve_voting_token(token_address)?; + ensure_e3_program_deployed(e3_program).await?; + + info!( + "Starting new CRISP round with token address: {} and balance threshold: {}", + token_address_str, balance_threshold + ); + + let token_address: Address = token_address_str.parse()?; let balance_threshold = U256::from_str_radix(&balance_threshold, 10)?; // We default to two options for the main CRISP app let num_options = U256::from(2); @@ -156,7 +245,7 @@ pub async fn initialize_crisp_round( current_timestamp ); // Buffer so tx can mine before window opens; end = start + duration so voting window equals e3_duration - let window_start = current_timestamp + 20; + let window_start = current_timestamp + INPUT_WINDOW_START_BUFFER_SECS; let input_window: [U256; 2] = [ U256::from(window_start), U256::from(window_start + CONFIG.e3_duration), @@ -204,12 +293,19 @@ pub async fn initialize_crisp_round( // since there are multiple steps (fee quote, token approval) that could take time. let current_timestamp = get_current_timestamp().await?; // Buffer so tx can mine before window opens; end = start + duration so voting window equals e3_duration - let window_start = current_timestamp + 20; + let window_start = current_timestamp + INPUT_WINDOW_START_BUFFER_SECS; let input_window: [U256; 2] = [ U256::from(window_start), U256::from(window_start + CONFIG.e3_duration), ]; + info!( + "Requesting E3 with input_window [{}, {}] (buffer {}s)", + window_start, + window_start + CONFIG.e3_duration, + INPUT_WINDOW_START_BUFFER_SECS + ); + let (res, e3_id) = contract .request_e3( committee_size, @@ -220,7 +316,8 @@ pub async fn initialize_crisp_round( custom_params_bytes, proof_aggregation_enabled, ) - .await?; + .await + .map_err(format_request_e3_revert)?; info!("E3 request sent. TxHash: {:?}", res.transaction_hash); let e3_id_u64 = u64::try_from(e3_id)?; info!("E3 ID: {}", e3_id_u64); @@ -235,7 +332,10 @@ pub async fn participate_in_existing_round( .with_prompt("Enter CRISP round ID.") .interact_text()?; - let url = format!("{}/rounds/public-key", CONFIG.enclave_server_url); + let url = format!( + "{}/rounds/public-key", + CONFIG.enclave_server_url_for_clients() + ); let resp = client .post(&url) .json(&PKRequest { diff --git a/examples/CRISP/server/src/cli/main.rs b/examples/CRISP/server/src/cli/main.rs index 4c6ba179b..65c39b3be 100644 --- a/examples/CRISP/server/src/cli/main.rs +++ b/examples/CRISP/server/src/cli/main.rs @@ -10,7 +10,7 @@ mod commands; use dialoguer::{theme::ColorfulTheme, FuzzySelect, Input}; use reqwest::Client; -use commands::initialize_crisp_round; +use commands::{default_voting_token_hint, initialize_crisp_round}; use crisp::logger::init_logger; use log::info; @@ -42,11 +42,8 @@ struct Cli { enum Commands { /// Initialize new E3 round Init { - #[arg( - short, - long, - default_value = "0x0000000000000000000000000000000000000000" - )] + /// Voting eligibility token (`MockVotingToken` on localhost). Omit or `0x0` to use deploy JSON / `CRISP_VOTING_TOKEN` in `.env`. + #[arg(short, long, default_value = "")] token_address: String, #[arg(short, long, default_value = "1000000000000000000")] balance_threshold: String, @@ -125,7 +122,7 @@ fn select_action() -> Result> { fn get_token_address() -> Result> { Ok(Input::with_theme(&ColorfulTheme::default()) .with_prompt("Enter the token contract address for the voting round") - .default("0x0000000000000000000000000000000000000000".to_string()) + .default(default_voting_token_hint()) .interact_text()?) } diff --git a/examples/CRISP/server/src/config.rs b/examples/CRISP/server/src/config.rs index c4f1a7984..d2bd976d5 100644 --- a/examples/CRISP/server/src/config.rs +++ b/examples/CRISP/server/src/config.rs @@ -20,6 +20,10 @@ pub struct Config { pub e3_program_address: String, pub ciphernode_registry_address: String, pub fee_token_address: String, + /// Eligibility token for CRISP rounds (`MockVotingToken` on localhost). Falls back to + /// `packages/crisp-contracts/deployed_contracts.json` when CLI init uses `0x0`. + #[serde(default)] + pub crisp_voting_token: Option, pub chain_id: u64, pub cron_api_key: String, // E3 parameters @@ -34,6 +38,17 @@ pub struct Config { } impl Config { + /// Base URL for outbound HTTP clients (program-server webhooks, CLI, cron). + /// + /// `0.0.0.0` / `::` are bind addresses only; connecting to them fails (e.g. macOS `EADDRNOTAVAIL`). + pub fn enclave_server_url_for_clients(&self) -> String { + Self::client_connectable_url(&self.enclave_server_url) + } + + fn client_connectable_url(url: &str) -> String { + url.replace("0.0.0.0", "127.0.0.1").replace("[::]", "[::1]") + } + pub fn from_env() -> Result { let server_env_path = std::path::Path::new("server/.env"); if server_env_path.exists() { diff --git a/examples/CRISP/server/src/deployments.rs b/examples/CRISP/server/src/deployments.rs new file mode 100644 index 000000000..3d4fad5f5 --- /dev/null +++ b/examples/CRISP/server/src/deployments.rs @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Reads `packages/crisp-contracts/deployed_contracts.json` for localhost dev addresses. + +use anyhow::{Context, Result}; +use serde::Deserialize; +use std::path::PathBuf; + +#[derive(Debug, Deserialize)] +struct DeploymentEntry { + address: String, +} + +#[derive(Debug, Deserialize)] +struct ChainDeployments { + #[serde(rename = "CRISPProgram")] + crisp_program: Option, + #[serde(rename = "MockVotingToken")] + mock_voting_token: Option, +} + +#[derive(Debug, Deserialize)] +struct DeployedContractsFile { + localhost: Option, +} + +fn deployments_json_path() -> Result { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + Ok(manifest_dir + .join("..") + .join("packages") + .join("crisp-contracts") + .join("deployed_contracts.json")) +} + +/// `MockVotingToken` address from the latest localhost deploy, if present. +pub fn localhost_mock_voting_token() -> Result> { + let path = deployments_json_path()?; + if !path.exists() { + return Ok(None); + } + let raw = std::fs::read_to_string(&path) + .with_context(|| format!("read {}", path.display()))?; + let file: DeployedContractsFile = serde_json::from_str(&raw) + .with_context(|| format!("parse {}", path.display()))?; + Ok(file + .localhost + .and_then(|c| c.mock_voting_token) + .map(|e| e.address)) +} + +/// `CRISPProgram` address from the latest localhost deploy, if present. +pub fn localhost_crisp_program() -> Result> { + let path = deployments_json_path()?; + if !path.exists() { + return Ok(None); + } + let raw = std::fs::read_to_string(&path) + .with_context(|| format!("read {}", path.display()))?; + let file: DeployedContractsFile = serde_json::from_str(&raw) + .with_context(|| format!("parse {}", path.display()))?; + Ok(file + .localhost + .and_then(|c| c.crisp_program) + .map(|e| e.address)) +} diff --git a/examples/CRISP/server/src/lib.rs b/examples/CRISP/server/src/lib.rs index 77898ef96..f83796ba1 100644 --- a/examples/CRISP/server/src/lib.rs +++ b/examples/CRISP/server/src/lib.rs @@ -5,5 +5,6 @@ // or FITNESS FOR A PARTICULAR PURPOSE. pub mod config; +pub mod deployments; pub mod logger; pub mod server; diff --git a/examples/CRISP/server/src/server/indexer.rs b/examples/CRISP/server/src/server/indexer.rs index cd4e713f7..875a1b49c 100644 --- a/examples/CRISP/server/src/server/indexer.rs +++ b/examples/CRISP/server/src/server/indexer.rs @@ -28,7 +28,7 @@ use e3_sdk::{ }; use evm_helpers::{CRISPContractFactory, InputPublished}; use eyre::Context; -use log::info; +use log::{info, warn}; use num_bigint::BigUint; use std::error::Error; @@ -240,17 +240,37 @@ async fn handle_e3_input_deadline_expiration( repo.update_status("Expired").await?; - if repo.get_vote_count().await? > 0 { - info!("[e3_id={}] Starting computation for E3", e3_id); - repo.update_status("Computing").await?; + let voter_count = repo.get_vote_count().await?; + let votes = repo.get_ciphertext_inputs().await?; + + if voter_count > 0 && votes.is_empty() { + warn!( + "[e3_id={}] {} voter(s) recorded but no InputPublished ciphertexts indexed — \ + skipping FHE compute (check CRISP indexer + on-chain publishInput)", + e3_id, voter_count + ); + repo.update_status("Finished").await?; + info!("[e3_id={}] E3 request handled successfully.", e3_id); + return Ok(()); + } - let votes = repo.get_ciphertext_inputs().await?; + if !votes.is_empty() { + info!( + "[e3_id={}] Starting computation for E3 ({} ciphertext input(s), {} voter(s))", + e3_id, + votes.len(), + voter_count + ); + repo.update_status("Computing").await?; let (id, status) = run_compute( e3_id, e3.e3_params, votes, - format!("{}/state/add-result", CONFIG.enclave_server_url), + format!( + "{}/state/add-result", + CONFIG.enclave_server_url_for_clients() + ), ) .await .map_err(|e| eyre::eyre!("Error sending run compute request: {e}"))?; diff --git a/examples/CRISP/server/src/server/routes/voting.rs b/examples/CRISP/server/src/server/routes/voting.rs index f96f0bd3e..0f1f06232 100644 --- a/examples/CRISP/server/src/server/routes/voting.rs +++ b/examples/CRISP/server/src/server/routes/voting.rs @@ -113,16 +113,6 @@ async fn broadcast_encrypted_vote( let mut repo = store.e3(vote.round_id); - if !has_voted { - if let Err(e) = repo.insert_voter_address(vote.address.clone()).await { - error!( - "[e3_id={}] Database error inserting voter: {:?}", - vote.round_id, e - ); - return HttpResponse::InternalServerError().json("Internal server error"); - } - } - let e3_id = U256::from(vote.round_id); // encoded_proof is already encoded in JavaScript, just decode from hex @@ -172,6 +162,15 @@ async fn broadcast_encrypted_vote( match contract.publish_input(e3_id, encoded_proof).await { Ok(hash) => { + if !has_voted { + if let Err(e) = repo.insert_voter_address(vote.address.clone()).await { + error!( + "[e3_id={}] Vote on-chain but failed to record voter locally: {:?}", + vote.round_id, e + ); + } + } + let message = if is_vote_update { "Vote Updated Successfully" } else { diff --git a/packages/enclave-contracts/package.json b/packages/enclave-contracts/package.json index 7df1e9c98..e6d14a4d4 100644 --- a/packages/enclave-contracts/package.json +++ b/packages/enclave-contracts/package.json @@ -159,6 +159,7 @@ "coverage": "pnpm test --coverage", "deploy": "hardhat run scripts/run.ts", "deploy:mocks": "export DEPLOY_MOCKS=true && pnpm clean:deployments && hardhat run scripts/run.ts --network localhost", + "configure:slashing-policies": "hardhat run scripts/runConfigureSlashingPolicies.ts --network localhost", "deploy:verifiers": "hardhat run scripts/deployVerifiers.ts", "upgrade:enclave": "hardhat run scripts/upgrade/enclave.ts", "upgrade:bondingRegistry": "hardhat run scripts/upgrade/bondingRegistry.ts", diff --git a/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts b/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts new file mode 100644 index 000000000..87471452f --- /dev/null +++ b/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import type { ethers as EthersTypes } from "ethers"; +import type { HardhatRuntimeEnvironment } from "hardhat/types/hre"; + +import { + SlashingManager, + SlashingManager__factory as SlashingManagerFactory, +} from "../types"; +import type { ISlashingManager } from "../types/contracts/interfaces/ISlashingManager"; +import { getDeploymentChain, readDeploymentArgs } from "./utils"; + +/** Proof types 0–7: DKG-stage proofs (C0–C4). */ +const DKG_PROOF_TYPES = [0, 1, 2, 3, 4, 5, 6, 7] as const; +/** Proof types 8–10: aggregation / decryption (C5–C7). */ +const DECRYPTION_PROOF_TYPES = [8, 9, 10] as const; + +/** `IEnclave.FailureReason.DKGInvalidShares` */ +const FAILURE_REASON_DKG_INVALID_SHARES = 4; +/** `IEnclave.FailureReason.DecryptionInvalidShares` */ +const FAILURE_REASON_DECRYPTION_INVALID_SHARES = 11; + +function slashReasonForProofType( + ethers: typeof EthersTypes, + proofType: number, +): string { + return ethers.keccak256(ethers.solidityPacked(["uint256"], [proofType])); +} + +function localAttestationSlashPolicy( + ethers: typeof EthersTypes, + failureReason: number, +): ISlashingManager.SlashPolicyStruct { + return { + ticketPenalty: ethers.parseUnits("10", 6), + licensePenalty: ethers.parseEther("50"), + requiresProof: true, + proofVerifier: ethers.ZeroAddress, + banNode: false, + appealWindow: 0, + enabled: true, + affectsCommittee: true, + failureReason, + }; +} + +/** + * Enables Lane A (`proposeSlash`) policies for all `ProofType` values (0–10). + * Local dev deploys omit this by default, which causes `SlashReasonDisabled` reverts. + */ +export async function configureLocalSlashingPolicies( + hre: HardhatRuntimeEnvironment, + slashingManager?: SlashingManager, +): Promise { + const { ethers } = await hre.network.connect(); + const chain = getDeploymentChain(hre); + + const contract = + slashingManager ?? + SlashingManagerFactory.connect( + readDeploymentArgs("SlashingManager", chain)?.address ?? + (() => { + throw new Error( + "SlashingManager address not found; deploy contracts first", + ); + })(), + (await ethers.getSigners())[0], + ); + + console.log( + "Configuring local SlashingManager policies (proof types 0–10)...", + ); + + for (const proofType of DKG_PROOF_TYPES) { + const reason = slashReasonForProofType(ethers, proofType); + const tx = await contract.setSlashPolicy( + reason, + localAttestationSlashPolicy(ethers, FAILURE_REASON_DKG_INVALID_SHARES), + ); + await tx.wait(); + console.log(` proofType ${proofType} (DKG) -> ${reason}`); + } + + for (const proofType of DECRYPTION_PROOF_TYPES) { + const reason = slashReasonForProofType(ethers, proofType); + const tx = await contract.setSlashPolicy( + reason, + localAttestationSlashPolicy( + ethers, + FAILURE_REASON_DECRYPTION_INVALID_SHARES, + ), + ); + await tx.wait(); + console.log(` proofType ${proofType} (decryption) -> ${reason}`); + } + + console.log("Local slashing policies configured."); +} diff --git a/packages/enclave-contracts/scripts/deployEnclave.ts b/packages/enclave-contracts/scripts/deployEnclave.ts index c73b94c3c..aebd36194 100644 --- a/packages/enclave-contracts/scripts/deployEnclave.ts +++ b/packages/enclave-contracts/scripts/deployEnclave.ts @@ -7,6 +7,7 @@ import { ethers as ethersLib } from "ethers"; import hre from "hardhat"; import { autoCleanForLocalhost } from "./cleanIgnitionState"; +import { configureLocalSlashingPolicies } from "./configureLocalSlashingPolicies"; import { deployAndSaveBfvDecryptionVerifier } from "./deployAndSave/bfvDecryptionVerifier"; import { deployAndSaveBfvPkVerifier } from "./deployAndSave/bfvPkVerifier"; import { deployAndSaveBondingRegistry } from "./deployAndSave/bondingRegistry"; @@ -253,6 +254,11 @@ export const deployEnclave = async ( console.log("Setting SlashingManager address in CiphernodeRegistry..."); await ciphernodeRegistry.setSlashingManager(slashingManagerAddress); + if (shouldDeployMocks) { + console.log("Configuring local SlashingManager slash policies..."); + await configureLocalSlashingPolicies(hre, slashingManager); + } + console.log("Setting Enclave as reward distributor in BondingRegistry..."); await bondingRegistry.setRewardDistributor(enclaveAddress); diff --git a/packages/enclave-contracts/scripts/runConfigureSlashingPolicies.ts b/packages/enclave-contracts/scripts/runConfigureSlashingPolicies.ts new file mode 100644 index 000000000..228c7ad27 --- /dev/null +++ b/packages/enclave-contracts/scripts/runConfigureSlashingPolicies.ts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import hre from "hardhat"; + +import { configureLocalSlashingPolicies } from "./configureLocalSlashingPolicies"; + +configureLocalSlashingPolicies(hre).catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/packages/enclave-contracts/scripts/utils.ts b/packages/enclave-contracts/scripts/utils.ts index eeb28c487..3a0e00ef7 100644 --- a/packages/enclave-contracts/scripts/utils.ts +++ b/packages/enclave-contracts/scripts/utils.ts @@ -52,11 +52,34 @@ export const isLocalDeploymentChain = (chain: string): boolean => (LOCAL_DEPLOYMENT_NETWORKS as readonly string[]).includes(chain) || (LEGACY_LOCAL_DEPLOYMENT_ALIASES as readonly string[]).includes(chain); -/** Monorepo root (`enclave/`), resolved from `packages/enclave-contracts/scripts/`. */ -export const REPO_ROOT = path.resolve( - path.dirname(fileURLToPath(import.meta.url)), - "../../..", -); +/** Monorepo root (`enclave/`). Works from `scripts/` and compiled `dist/scripts/`. */ +function resolveRepoRoot(): string { + let dir = path.dirname(fileURLToPath(import.meta.url)); + const root = path.parse(dir).root; + while (dir !== root) { + if ( + fs.existsSync(path.join(dir, "circuits", "bin")) && + fs.existsSync(path.join(dir, "package.json")) + ) { + try { + const pkg = JSON.parse( + fs.readFileSync(path.join(dir, "package.json"), "utf8"), + ) as { name?: string }; + if (pkg.name === "@enclave/main") { + return dir; + } + } catch { + // keep walking + } + } + dir = path.dirname(dir); + } + throw new Error( + "Could not find enclave repo root (expected circuits/bin and package.json name @enclave/main)", + ); +} + +export const REPO_ROOT = resolveRepoRoot(); /** * Default insecure-512 / micro committee layout for BFV aggregator verifiers. @@ -119,7 +142,7 @@ export const BFV_DECRYPTION_SUB_CIRCUIT_VK_HASH_PATHS = { export function readVkRecursiveHash(filePath: string): string { if (!fs.existsSync(filePath)) { throw new Error( - `Missing circuit VK hash file: ${filePath}. Run pnpm compile:circuits.`, + `Missing circuit VK hash file: ${filePath}. From repo root run: pnpm build:circuits --preset insecure-512`, ); } From 201c73cc0eb6b0030e7c6076b86038baac37fea3 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 16:54:58 +0200 Subject: [PATCH 32/54] improve and fix benches --- circuits/benchmarks/README.md | 81 ++- .../results_insecure/crisp_verify_gas.json | 176 ------ .../results_insecure/integration_summary.json | 171 ------ .../benchmarks/results_insecure/report.md | 140 ----- .../benchmark_run_meta.json | 11 + .../crisp_verify_gas.json | 107 ++++ .../integration_summary.json | 244 +++++++++ .../benchmarks/results_insecure_agg/report.md | 213 ++++++++ .../benchmark_run_meta.json | 11 + .../crisp_verify_gas.json | 88 +++ .../integration_summary.json | 180 +++++++ .../results_insecure_no_agg/report.md | 186 +++++++ .../results_secure/crisp_verify_gas.json | 129 ----- .../results_secure/integration_summary.json | 171 ------ circuits/benchmarks/results_secure/report.md | 141 ----- .../scripts/benchmark_output_dir.sh | 34 ++ .../scripts/extract_crisp_verify_gas.sh | 46 +- .../benchmarks/scripts/generate_report.sh | 500 +++++++++++++++--- .../benchmarks/scripts/regenerate_report.sh | 34 +- .../scripts/replay_folded_verify_gas.sh | 2 +- circuits/benchmarks/scripts/run_benchmarks.sh | 84 ++- .../scripts/sync_bfv_vk_binding_fixture.sh | 4 +- crates/aggregator/src/publickey_aggregator.rs | 19 + .../src/ciphernode_builder.rs | 35 +- crates/keyshare/src/threshold_keyshare.rs | 65 ++- crates/multithread/src/multithread.rs | 29 +- crates/tests/tests/integration.rs | 284 +++++++--- .../src/circuits/aggregation/node_dkg_fold.rs | 47 +- crates/zk-prover/src/lib.rs | 3 +- .../scripts/benchmarkGasFromRaw.ts | 36 +- .../test/BfvVkBindingIntegration.spec.ts | 2 +- .../test/fixtures/bfv_vk_binding/README.md | 2 +- .../bfv_vk_binding/folded_artifacts.json | 8 +- 33 files changed, 2129 insertions(+), 1154 deletions(-) delete mode 100644 circuits/benchmarks/results_insecure/crisp_verify_gas.json delete mode 100644 circuits/benchmarks/results_insecure/integration_summary.json delete mode 100644 circuits/benchmarks/results_insecure/report.md create mode 100644 circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json create mode 100644 circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json create mode 100644 circuits/benchmarks/results_insecure_agg/integration_summary.json create mode 100644 circuits/benchmarks/results_insecure_agg/report.md create mode 100644 circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json create mode 100644 circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json create mode 100644 circuits/benchmarks/results_insecure_no_agg/integration_summary.json create mode 100644 circuits/benchmarks/results_insecure_no_agg/report.md delete mode 100644 circuits/benchmarks/results_secure/crisp_verify_gas.json delete mode 100644 circuits/benchmarks/results_secure/integration_summary.json delete mode 100644 circuits/benchmarks/results_secure/report.md create mode 100644 circuits/benchmarks/scripts/benchmark_output_dir.sh diff --git a/circuits/benchmarks/README.md b/circuits/benchmarks/README.md index 9d3a11009..6531de85c 100644 --- a/circuits/benchmarks/README.md +++ b/circuits/benchmarks/README.md @@ -19,31 +19,71 @@ From this directory: Options and secure-only **config** circuit behavior are documented in the script and `config.json`. +### Proof aggregation and folding (integration) + +The gas / integration stage runs `cargo test -p e3-tests test_trbfv_actor` with **proof aggregation +enabled by default** (`E3Requested.proof_aggregation_enabled = true`): per-node `ZkNodeDkgFold`, +fold attestations (EIP-712 against `DkgFoldAttestationVerifier`), and exported folded +`dkg_aggregator` / `decryption_aggregator` proofs for Π_DKG / Π_dec on-chain gas. + +| Flag / env | Effect | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| `--proof-aggregation on` (default) | Full fold + aggregator path; folded artifacts in report | +| `--proof-aggregation off` / `--no-proof-aggregation` | Baseline without node folds / folded export | +| `BENCHMARK_PROOF_AGGREGATION` | Same as above when calling `extract_crisp_verify_gas.sh` directly | +| `BENCHMARK_MULTITHREAD_JOBS=N` / `--multithread-jobs N` | Rayon concurrent ZK jobs (default `1`) | +| `BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER=0x…` | EIP-712 verifying contract for fold attestations (default: localhost deploy address) | + +**Default output directories** (under `circuits/benchmarks/`; aggregation on/off no longer +overwrites the same folder): + +| Mode | Proof aggregation on (default) | Proof aggregation off (`--no-proof-aggregation`) | +| ---------- | ------------------------------ | ------------------------------------------------ | +| `insecure` | `results_insecure_agg/` | `results_insecure_no_agg/` | +| `secure` | `results_secure_agg/` | `results_secure_no_agg/` | + +**A/B comparison** (from `circuits/benchmarks`): + +```bash +./run_benchmarks.sh --mode insecure --no-proof-aggregation +./run_benchmarks.sh --mode insecure +# Compare results_insecure_no_agg/report.md vs results_insecure_agg/report.md +``` + +`report.md` includes **Audit status**, **Measurement methodology** (metric kinds), labeled **Role / +Phase** rows (`wall_clock` vs `isolated_nargo` vs `tracked_job_wall`), **NodeDkgFold sub-steps**, +and **Folded on-chain artifacts** when `integration_summary` is present. Verify gas must be complete +(not N/A) for audit sign-off. + ### What gets stored (secure / insecure) -A full `./run_benchmarks.sh --mode ` run writes: +A full `./run_benchmarks.sh --mode ` run writes to `results__agg/` or +`results__no_agg/` (see table above): + +- `raw/*.json` — Nargo timing + artifact sizes (source for the **Circuit Benchmarks** table) +- `crisp_verify_gas.json` — verify gas, calldata gas, artifact sizes, and **`integration_summary`** + from `test_trbfv_actor` when gas extraction succeeds +- `integration_summary.json` — snapshot of `integration_summary` (phase timings, folded proofs, + multithread / operation timings) +- `benchmark_run_meta.json` — CLI flags (mode, proof aggregation, multithread jobs, verbose) +- `report.md` — rendered summary of all of the above -- `results_/raw/*.json` — Nargo timing + artifact sizes (source for the **Circuit Benchmarks** - table) -- `results_/crisp_verify_gas.json` — verify gas, calldata gas, artifact sizes, and - **`integration_summary`** from `test_trbfv_actor` when gas extraction succeeds -- `results_/integration_summary.json` — snapshot of `.integration_summary` (phase timings, - folded proofs, **multithread / operation_timings** after a fresh integration export) -- `results_/report.md` — rendered summary of all of the above +Older runs used `results_/` without the `_agg` / `_no_agg` suffix; `regenerate_report.sh` +still finds that layout as a fallback. ### Regenerate `report.md` only (no integration re-run) From this directory, after you already have `raw/` + `crisp_verify_gas.json`: ```bash -./regenerate_report.sh ./regenerate_report.sh --mode insecure +./regenerate_report.sh --mode insecure --no-proof-aggregation ``` `crisp_verify_gas.json` embeds the integration timings; if you also keep `integration_summary.json` in the same folder, the script passes it explicitly (useful when gas JSON is missing a field but the snapshot is complete). `regenerate_report.sh` itself does not re-run `test_trbfv_actor`; it renders -from `results_/raw`, `crisp_verify_gas.json`, and (optionally) `integration_summary.json`. +from the matching `results__{agg|no_agg}/` directory. ## Refresh after parameter changes @@ -60,13 +100,13 @@ pnpm -C examples/CRISP/packages/crisp-sdk build # Extract on-chain verify gas from simulated verifier tests ./circuits/benchmarks/scripts/extract_crisp_verify_gas.sh \ - --output "./circuits/benchmarks/results_insecure/crisp_verify_gas.json" + --output "./circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json" # Regenerate report with gas values ./circuits/benchmarks/scripts/generate_report.sh \ - --input-dir "./circuits/benchmarks/results_insecure/raw" \ - --output "./circuits/benchmarks/results_insecure/report.md" \ - --gas-json "./circuits/benchmarks/results_insecure/crisp_verify_gas.json" + --input-dir "./circuits/benchmarks/results_insecure_agg/raw" \ + --output "./circuits/benchmarks/results_insecure_agg/report.md" \ + --gas-json "./circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json" ``` If Π_DKG / Π_dec **verify gas** is `N/A` because `crisp_verify_gas.json` came from a failed extract, @@ -77,7 +117,7 @@ step and merge **dkg** / **dec** into the gas file (no Rust re-run): # For secure folded proofs, align Solidity verifiers first (--build may take a while). ./circuits/benchmarks/scripts/replay_folded_verify_gas.sh \ --summary "/tmp/summary_secure.json" \ - --gas-json "./circuits/benchmarks/results_secure/crisp_verify_gas.json" \ + --gas-json "./circuits/benchmarks/results_secure_agg/crisp_verify_gas.json" \ --build secure-8192 ``` @@ -87,13 +127,13 @@ If `crisp_verify_gas.json` has `integration_summary: null` but you still have th ```bash ./circuits/benchmarks/scripts/generate_report.sh \ - --input-dir "./circuits/benchmarks/results_secure/raw" \ - --output "./circuits/benchmarks/results_secure/report.md" \ - --gas-json "./circuits/benchmarks/results_secure/crisp_verify_gas.json" \ + --input-dir "./circuits/benchmarks/results_secure_agg/raw" \ + --output "./circuits/benchmarks/results_secure_agg/report.md" \ + --gas-json "./circuits/benchmarks/results_secure_agg/crisp_verify_gas.json" \ --integration-summary "/tmp/summary_secure.json" ``` -For secure mode, use `--mode secure` and replace `results_insecure` with `results_secure`. +For secure mode, use `--mode secure` and the `results_secure_{agg|no_agg}/` directories. ## Reported protocol tables @@ -107,6 +147,9 @@ For secure mode, use `--mode secure` and replace `results_insecure` with `result - an `Integration test` section (end-to-end phase wall-clock timings) - a `Thread pool` section (Rayon threads / cores) - `CPU-bound operation timings` (tracked in-process averages/totals) + - `Proof aggregation / folding` (enabled flag, fold attestation verifier address) + - `Aggregation / fold operation timings` (`ZkNodeDkgFold`, `ZkDkgAggregation`, etc.) + - `Folded on-chain artifacts` (byte sizes used for Π_DKG / Π_dec gas replay) ## Derivation rules diff --git a/circuits/benchmarks/results_insecure/crisp_verify_gas.json b/circuits/benchmarks/results_insecure/crisp_verify_gas.json deleted file mode 100644 index e5c316eae..000000000 --- a/circuits/benchmarks/results_insecure/crisp_verify_gas.json +++ /dev/null @@ -1,176 +0,0 @@ -{ - "verify_gas": { - "dkg": 3042552, - "user": 2973037, - "dec": 3553605 - }, - "source": "folded_proof_export_plus_crisp_verify_test", - "artifact_sizes_bytes": { - "dkg": { - "proof": 10944, - "public_inputs": 480 - }, - "dec": { - "proof": 10944, - "public_inputs": 3552 - } - }, - "calldata_gas": { - "dkg": { - "proof": 170088, - "public_inputs": 6144, - "total": 176232 - }, - "dec": { - "proof": 169908, - "public_inputs": 17304, - "total": 187212 - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.113595944, "runs": 3, "total_seconds": 0.340787834 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.606440319, "runs": 3, "total_seconds": 1.819320959 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.558063459, "runs": 1, "total_seconds": 0.558063459 }, - { "name": "GenEsiSss", "avg_seconds": 0.124646278, "runs": 3, "total_seconds": 0.373938834 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.227111444, "runs": 3, "total_seconds": 0.681334333 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.433242125, "runs": 1, "total_seconds": 8.433242125 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 49.593938792, "runs": 1, "total_seconds": 49.593938792 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.539657042, "runs": 1, "total_seconds": 20.539657042 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.48974143, "runs": 6, "total_seconds": 8.938448582 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 62.378365152, "runs": 3, "total_seconds": 187.135095458 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.134972792, "runs": 1, "total_seconds": 2.134972792 }, - { "name": "ZkPkBfv", "avg_seconds": 0.340530111, "runs": 3, "total_seconds": 1.021590333 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.378689777, "runs": 3, "total_seconds": 4.136069333 }, - { "name": "ZkShareComputation", "avg_seconds": 2.756735736, "runs": 6, "total_seconds": 16.540414418 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.547185821, "runs": 24, "total_seconds": 61.132459711 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.14141, "runs": 3, "total_seconds": 18.42423 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.097622333, "runs": 3, "total_seconds": 0.292867001 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.228617342, "runs": 5, "total_seconds": 1.14308671 } - ], - "operation_timings_total_seconds": 383.239517716, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.049949667 }, - { "label": "Committee Setup Completed", "seconds": 20.248450958 }, - { "label": "Committee Finalization Complete", "seconds": 0.005924208 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 305.202475458 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 307.797721583 }, - { "label": "Application CT Gen", "seconds": 0.3082625 }, - { "label": "Running FHE Application", "seconds": 0.003257541 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 79.923254834 }, - { "label": "Entire Test", "seconds": 411.343670791 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000003f7062d91e599a1d9000000000000000000000000000000000000000000000008cc7102c928c65c450000000000000000000000000000000000000000000000075ba3dc02d69ce9910000000000000000000000000000000000000000000000000000ed71a67536b000000000000000000000000000000000000000000000000e44e8edaa774d8d700000000000000000000000000000000000000000000000069df7c477ac356a5a00000000000000000000000000000000000000000000000546c35bc9a1e69c5500000000000000000000000000000000000000000000000000021ee487cecdf100000000000000000000000000000000000000000000000ab881c361ac7b250800000000000000000000000000000000000000000000000615484820700c856a00000000000000000000000000000000000000000000000bb5ddeeb0299d72f60000000000000000000000000000000000000000000000000000614a8dc34464000000000000000000000000000000000000000000000008f86b47c227146d86000000000000000000000000000000000000000000000005b0124c6d784e70a800000000000000000000000000000000000000000000000c7ce22f6009a0150300000000000000000000000000000000000000000000000000028c352ad51c1f0a1e934d28e26ddbc2239f70f1f2a218f011eacde911ac55ca4f56e4c92826ba0151ec8baa1515407b51df92e0368977054f4ff1d48781d8c1bc7962286ed7d20c520eaffc4f8e6351751bd8fe1e162471d0fe3163d177d0ead3ac22d2519a17089d86113a4a8393c93b80f3bb7c9dd5bab24c560c9f0d8906322ab4d14bdf2527575e8ae48fb40af0adc9c7df54fcb65138a862afbaf04d592215d462bcd29107c4ee919449aaed15296c3db7bc5d246d1753696a3fb0d71ae9e02085318c402afd41a4633e857acd0967c13bf26c50c6457ab5dd898ac1864799b9730689d52ad93c530511536a3714b5748aa649245ab18e286181818ec79f628930d4b39d1af275a9bbc79f11513fc819f8a77429a99f5c19dbc3aaa699028fd84578ae771d2cb42f9080e97577d5433096dc7b5872c8a57058cf2b02f8c0e531169ac86d010bdbafbdca84bfb0e04bad65fe3350d55fbfd17c3806f415f2fef12c85a5cb05693555777afb1ed8c9e98ab2221191471b57f70880883d7f77cf7774d4fa8726d1e7542a8d79306c76a221eaf68d96dcc25f7bfa67547c7e7e6823a8f5036a0520516bf26d60b94a8dc1de281b183f97c760db8ac7dd87c986a7ab87c57a3521148a502af09952b50e41ce4798bccddf61aefc5c08cbe35204f43d0c36954a0e89229a121eb3d18ac0c9561aed66af56e683b1ba590eaa4edc1d6476c2762208096dced400b6f35fcbf000acf60ad54d9076eec61d80121dfff0afe26e749c2c7dc41f5ec8f3774ba1084aa7c4d494798c31c4b33f0371083a13e67aaaa9fb297e0ddb44886687d5d35b7485cb47105303ab9d1a237f8dd9dcb93298077c2c1bf65619bd87d561ea96e41c5106896c2a1307eae3afc8ce2c501e6ae5183d9b302f94e692484cba7c25419a42dac6badcb139955e149eccdee3c1b2bbb39bd92006d24ae9e0b0607f5c1994f5c0eec60fdd95dcd71c256fde7e12934ae95ff62c1c3afe38e4250234c03b3fbe574ebdc4b6acce335cffc79409260039c2c5890f33e7fc5c1904725888d81fbb00dfe5829b22b86c60cc60282639614406840619a2a11d45659615c74b56c03d94902360a16b8813b318f90cfdf6d941bd27c11c2c40020df0c8df1b5f56cf8faf6e3f78bdd34f134ab0857d93cc7fc0d8d26a084aeb7c91a1d79ae529ded891a7a2a94b774927a6747644bc87afa45c115cef199c133466b246d8a8f6a87f50454203689ffe4a63c722e36cd714d94d5d8ec715203e52c6915f66cbd5ef50437c703b5c3393cc645517d6c2a210f9ac23d88a2d480d46aa3febce868435cb4052c6af52d836aa60a7141cd972100fdea65c6623f51577a4fdabe52375af64863d09b294de12c786066a4510cf18737b5cb2521bbd58b29c859db5be07fa8057c61e27972541464105d7346270eb1eaf8183f00b03e6634f391192f98965c1fe150acaf8decb6acd8ae9578aba334c8ceb816f220f0a141a0b9c666e712d8917ceae41edfa816a4a3cc319116df04818fed2c5199163f3eb27e7de5e4d47b7a22a1e7e7186dc3a4ec5744e6b26dc0ac4fe236317e300bfa4ac8d1c52a22a867753dbec2a0ef3ea56b28a978dc21c6d56e0a9822cab56055b0a192a4f2a1fa9a1e2ad0d98fb85051bad9fcefaff281aeb31781b070f5b929a644a885cc948d2b8e9b7948b588a829b836bd61f9087f753533e5c0e79b5fc84e0de7671e00294fc3a78e2ad1e7d469a0e2cbc379f6494954a946d23e35a296d5c94f4cf03f663f7328be01a3aa375f6adfc6e3e10f89456df78a3240903d00116a9b3572d29437bfede50d55590b4df762150fbfdd6fee2c519302c37cdbd0ca0131da0faef83a2b57f89150a76f67a22fb2825ae844a2c0e55a62c00585cf13f7c290242ac5610faa0fee3018541c5733b68415f6f999951b90921e98735f1adb294593b17e6678722213e8c446ad3a7ac022936b20a6d77b0f6176f505aabdbc699bbc6e19d082fb8318459555069abbba3d01c1b9b552da6c8174ffa28093ab52aa34c5a04e72119ca3dbeb427c31876f8a012fa6be8dbd2a81076baa410047ec186a7ca713a00714cf370d06de1847c65f81e0023042b1f491eeeff34d3b6e787870dbef2665bc02dca4ec8c0fddf496de2e31c7450d626fd10ea411776b22e3ceb7f3891f81080eabfbe32548bb88663fa270ebba72fc8c5243643188bc9ee4d07de409f501cbfa49afec7c87d1f2789d2f932f298ccc33c0fb18246887d583e8a5966746fb3bae0cb7f3053e7d807c47ee679166cfac9f11031de5328aa1e8cbbfe748655b5ac3f7b303a92328c64d738e337e2c88e4b1815aa4e0f6573d470282ce50581e75a13f328a3a0b9a1c263f5180223110576da26d8a27a0e4e674729f076e96b629ad9b2501d8f8d437d8ed00e908692403aa12b370df72df67acd5d66fbd6d6e9f3341101c4fe455c6429cbdc3d843b52ae36215a43ac8698db6dcb1ce12497b9f51d8cd0c4b78b96bd81f24d574177a98f2c19090146ab59f7d5f461a0c6bdad1e75b6da38e9147b88a655ca582581ad2b64274c7a63e1cfd278616f7d185902cc57cd18f89bbb4ce2dd17cecebad3c3ae812450131532f01a9c5c62f6bd9dbd817f5b7a07ed301ed9d92083d0724cf8a2221e263a087933e462f1975e9d2aef17772cb92841fe96566553bf90ad051da81e0bb61346c75d7de2a99a99001e56bd9dbb08dab169ec147bd7da07e01518d6951476e1758afce02df80c3fbdc515f04cb5bac48c201aa8b1fe54a520908d01d71def68a4993955aee64e9ca2d4b85bff5404f51696a7f26ca335b112900df31029b365ff086b60ab3d13c2c44465a1656c82a1472c6471bd0a018542139b4bee2741b75a8797dca4cdea4df105561471e1925e46a05a495ccb95a62bda8f2fa905bf990e5f7a30b9ec101e503b1c44ce94d6d751db5d4d01adb185e7e3de9f27268e33e71fc0136c349931194a638570f3771419d2b2dcddd74580e61cd0e1d82651a4f74f8bcb480b02daa45a841fc3f76890eff255b85a88bc204442cc35b501e65b950d5618397f528e8835d8167c888594c0b3d3d56335e4968440b7579c18a71da291ff2dd20bff984b0809d0a72ffa9aa15e477ab40e998e1845754979039b052e98b768bd8ec0df44278c207269026354eefbf6d32494347009c580731b668298f1615b5fd77cf4d43bf2f6319db631859267780752e728fb52d39eaa1f8794c0aabcd93d68844a49b146200a8dae0284da464d105fb70dff102798180231d29917b244496fab251ab80197d5b680b97fc6560a74d4627bc8702da1430dd7c2e80e72a633dc52613da653e24c43173f0374fb9ee7c31d45109a2f2dda1a6ed6e02c387274f3dd41d3d30ebbe2d0e5be93a288321e54eaafee3ae3408d2a209a285a1286870d7081d8d7987d96c0d96ca7d38d53de6db74497ebe57c97239e24d134e668da1e614fc31b92e571228eeb9eeee087b5e87183899701652d1223a0c17880be1879a41e3e4444a426ec0e9fa4af9628907a42e4a40d9aeb57244d40642f454a684f07fb45974600f06e72ba7ae4dc8ebe05a4346ff12279811e1e8ac8dabd36dc3b58fe17eb6bda441f9d75fae5a431f131ef567c49334a2026b1b06d0d09e4dc77fd44f9bba7003e5f6b6b6a78d19cd2ddea68492d8d5cf918fd982caf0cf885d3a0819ffb8da3d24352207baa1802784bde523e1d4071772552c303070de53ae5a62aae59b858aed5260f66cf269d2fbe57df7c3960c84c00ce310e336aadf9d8ac732b8504f61775296afd57ac458c4b3f587e9095473628876de23bdcea9df2fefd44148b794984b966027bb3835f4deb5d0323e3d8512839d9d7e91bbd6d0b579a8269256fadf864f388ffca58afddd505f1009a887403f654353a693b5449c09424816a4f223999c0261b85fc9e27f8044a685fd97e21c348d3131cd0ad8ab11361ad8bd542e13b95cc94bfc6f4e02864e77bc3a5ed27eadddc4e835359c11b295e862d4647e0be8151f1472a2bad617d662049c258270ba05cc3c8fee60b79dbe3cdd5892d467eb1eb919b10908c1e6bce7cd6256f08b90d498edff9b990d45e8517e68b155bde445476e86361a62bfca66c782d9e1d5020f5aac68add0d8d5e11313bd34806eb1d07998bac8aac78045bf62ef35d1e6c68699ae309a357eb91767cc97f41afc839fc0e2acf149f03f5e4768ffe12239d5ec835b63dedd8ef257ae0ecf932ea82089a9b334f50443d89da8b9d940d17cf45a436d206057e1baac60dd3d4fd5053184b5fb725563e2f5dac366e47a41671e27bfa1af5d7604965af6f65f9cbc0e187826e3034396ad39269a7828deb009d86c1876e90e2bdaf2310456e63048c0ad17f98d7e3405a57c62f0891862c22868eac6f10b58ad7476cedf652fcb48c170c458ea95e0dab1300d1477d371a0a83855b2b4be0f913d533698dc00b61e6e0d98172e11cfecd9f2ec88fe7e1282a8e3523974469c524c395b19cd1b2ae2e6a0f08b8b7e9925dd16241984f02fd028654ca0f201085d96833d6c571a08c2b680b77e6374767fd732a315e12b8c0150e7ce2da2707c402b007853edf703e5a06659abccc5c7d539a6303c10a747a1e1e6c660a3493a44ad142c672094981fe9b0699eb6202ee481875332cde412924e4d4f54c75ba290478813ee7b7f8d2cb73c42bdc9b4db044a2c033356a397d2c4c27dacccc0604af0ab1b240b25d79bd8efba3cc58a7d4e98a56ef93e694d61dd9e5f7b64e66744fba85443990e2ed06eec8d4edba99ed139bc3fc816ff1e925b004f64a5e0fad8b437791220910fb7619b648984bb76620b9995c1e0a69a700c51f7b25bbc228f229520d7655200d19e0e155637260069e8ecaadaf56dfda0b606943398c2acd8244ef5e50f797377dfc6fa9ee06f4275f03912c8b039a4e10545dafb7eb5d56f282fddbe00bfa22c42731fd13dcd08237f88c870a6b02d821c0cc4c3cbaee07da622638faeca1c035134da588eb3a47298ff1f76c5bcb1b07713872aefcf57c09befd5274046db1e94bbf346a9618cec87c5d833b3cb6bd1fb1d5fe9d37ad9b8f7d69d27e74ee6d11442b4072971d6e308c90d2f28f692c154ec89bf6cbdc150ce4340973cbcaf071d3a6b86f43c96c95bb68fe435b719e204f06a549b9859201bfb3c8f674334d15b92441bf420dad6b8ba7ed93a4b10d2446d9f477b8f4ec9d7d61dd89ffaf24851b9e1af79ef09d04fe4c7d3b8aa5fc0da2c0013fa968fc23f85734436cd57b203429df6c86e228b25d897c432a70cb14e82c91259ac6b99a472f04cb05da560a649d84b1d96e532adbd39ce1d118e024519d5d192db13745d58bd16158c16f79f890268fe33acf3640cd7fbd97228a0ad8f596ad7c30f5fd9a30c3a58d741a2d1248c01cf56fb12d646351b1d7bdec09b42a222f01fd7c10403cefcf03c0fea4708557da361d24f964088d362babea2032fff91add268b7e2260474cc0c1aae1ec3526be33d9724af9298ae7e94117125ed9034842758be7da9d9f46146f157bcdf61f8f0ce96b32970cf633a17db02a03183311f34d9192fb47068e905383e1fb9a893dbe99e7852c091eb056d2df2a36fcf2ea043a1f9f80df7d2e61ed2d3f3294a2bd6ce92f31dc6d8bbdab4f6d1c9e678355d1fcf76296effeb331d4e2fe67a71da03da01f1a18cf069553eaaa13980e46467f02c69bea54c3720548b3123a35dc4d419d66c373b9089ce74d2f1c887e07f9068b2b4151380e03ab3782c4d3477698d60306b49d96880133708412e7aa8c948838ad2e7c574a87420d2eaf8a25535161adfaf0c5dc8f246e6f831f0b0d07d5c1786ecabd3ba2c3c693d53d95d3c61376e795acc867eeedc7c22e069b5d13c0155fb52b2f949bacdfedadeebf5db8d0bf6c78a11124a650873d00014105f5c4d5e9a76723d3c67939a223b0e21dd47410856f6109731f3fd4573914528d4d0451b6e9852c06874f6a8f499644f82ff8016134533e19e7543937fa1b79e556eb2a677f43c40ec09e8c2385b5e5dda6215ab070253c5d3e53b2ff7a00a2bc71a7012e60f4ac98fe1840e7be8c09aa648b3ab0f0c27085569d6460b8028f028a0fe35cbc839b5c9e28401bda72c3972c2888d203f378d76751e957740c1177fdc8d98ddc08e28b05c069efa51c8252cbe0cf818abfcd395484981c0e2b80cc558a8a10aab8b962daf92a786433511af9f8527aa2a49c2b8bd53101d105fc145055d9ba564709104c5549bb24a3dd91a505e47959f719258a1cc6dccd06c3b905097f428b5cff87707bd0e1ce71ece41f31ad817ff1f3d04630274e7707bac1c17fe327bb81353d07980eb53d4602c580dda7f7f15e59e57cd7dbd3d90613f1fd884030e3bbfcb0d7efdc94158660e2fa5c7d49d11e08a9b723087892209124075b7d82394c4ac6a6a3e6744b706cf7f538fbe24358c280b1c6d241ec04a10e305eb956910f0d968d0f83280049572e3e7206b91ca83f558ff6a01694227174a5061617717a39664d6b54a851e1ab5c9041c80952a220f62d183fd4ff02f9d499f6808334fb92a96a4a1332be8b96b264d7ace5e3dde89aa066da81471f8dc4f4c40b4ff98e4b85557de6e994baccc86846ab05cd2e6ab2f7affcf99c008ccbcbedd42d97aa344146ad1359db6ef1c3c8e7ee17fbbadcf7d53c7b749b00d57fead071621cb4046c8d9c40b138b1e897b30d7872b654bf975575b18b540e2b702474b75054fd96c52543d85e68751471a807905b532f7b72afa62bc86c08d20514704803dc419c284bc0b953f95b2c5c957979546e79b46c2596fcd7bb07804adccc6532e423df312c056d852c6730935f95c1ff85556161503263da9e10b45be2e0cfbcc72420fec0109fef3fc1d69a1610ae256cda6fa0b1edb22e6325599a344ed9500bb3b6db80b3ed4b856d798aeb51be3bacd83e3ece79a68d750423ee12cd58ed06961b157814caebef95e7bb0384213e8f34c3f364f7e0479b248d3ce8d2005755b5832407fc03cfdfd570903d70ab82f55e01499f0e533dff1d1ce6f0c124b0fa6dd880e2c715a08bd9834659fb5dad9de7e79fed5471144c0caea76eb72fd11995bbcd26b93c3eeef7bf4613270088c9d292c5b16a2b9c5f064ed3eb9f013c40d3b53e08486724ba493c06a4d9ec7d768aacb0ae7e9168c0028372c10a7600ef9e464c2617a6185c7f9f1517cf7925c3516a0bf6a8f6e7e229a8fa104e164b8cb328d16eb11f3ea40f968302cd6aeff674f113a0d5f4eec61d57142b4efbbaf733db8415459c129ac5721a1b1bea5a025da135349fef2e86031668421ede6358b63d5078c2bedf744d0b9304603ffa2726d02e8c9bedcddb10e57654bddbf2ff23134fd892ddaff5e13c7571c673578d336aa4cd6f7ca9f30fa7ff2cf4ea874d55be1e755b749a2246ca93b7bd613e24c52ecd0e0190563713f2df9406088e6b2cd92d9b03b4e3affe99b7624321bd6b462fbdf3235bcfe52871605f0cada9534cc6bb07b31b133dea6b0f533aeb70ec48236de198c427080056e3d16a420157d335b08369f69b8897645453e8087753b82fcf8522406be11fbef23cf74e343dd70144cfa5ee7e67ab40a2dc21902315b46772eb4275bb841254da0b779691c5f56122b367f86dff07a34bdbc053a9e56dfc4afd69fa3609198d4cc852a9b2707922e89d1b91ebb02dd602dbe9cdcfc74022e67aa0eac4fd1261d7f338e3f31aa22e69b5d4217900caf7606bac392cb7b46b96b21305ed0b0781389ac7d3e23b955e9358d57eb4735c8ebaad59d1f2349dcc16bdaae8c0081d29f21df52ff42d437137ea915aa96d1e286e1a88de177775f59064fb4a84fe06aa67f3a50812921752d0fc249387a70a15aaf42811dcbbcde646c49f404e8f09121fd977cc344c9126d5a8139cc5afa4c15e555805e2edb6fe4a26460990462fabe10ea395785faa72127618e810bdabf626a3e54f78cda87a4d3c068c09e10663bc532763b0861d40cbe1eb9e8733ffc701df12c7c72a824b1c581aec615a1041c48075e3a7ea820617fc48ae9a815b01b78ca7dacab62c9f4b32028d8e0e0d15d296790ffe2507bfab1653f0e13bc3e70337762110b30e0f2e2c7a86d2ac0bdeb5e0f0ec18899aac0ae393b62e1727b189591efbaaa3853735ea9073d90115f3212a4b7d53472fccd625def7ea36ec894cb964a6dfc9a7ccb369d549c0a125b1f2f6f5fd4fa21b90dbe622eec58efbb1705e26cc444bc5aa0671d1b68d961bb5706653c96d8875eed434702544d6381308c43e8fddb0a5640340a64a287b20ee4d26b56ebf145ea85a4315980f5c1f4e64f97d3ee7269de80f1d5947c1a61fe375768d34e3151255e55d0f6857e895d118541da36e13ebdf6eee9e981dcd0d9b0fb6f9fab694ba9973b04780ae2729596e3c425dc871a404a2bc36bf5a7300e0cc098986f2091345a77f699bf2eb03d648c8f8886e8e103faef891f1f3430d57c6d441288ca100d3cb75b8e3197a571fef30525f4b542350d71170de7b63197434f4adf3c4383e89d67c298c03447e65f8b572898d86bcc8df7d5df4110815a16f3839c72f3ac89b895876947140d4aee3cf1d7ef24a44d41703097a6c912c783b7b2a2d525b86be5b993cee26e637b892bd6233a46d0412808ed53309a126051ffdd0d5e0ba154d1f3580a24c5896d172b523c26bca9f2cb842195ff17803998f55cb444e663539aebb346cb75f5dd2f096ed1989947829c17059d40d3121f726cfef977699f9f5f4c66b9073262f539766dc48a053936113d6cb284ef3182d0a64eadcc863d7267978ca1d1afd73412e3fddd3be739603e57cb3ab538b2daf94efc9e5d3473bee3b714734a85db1367c08b05bad992d964802caf16e360e0e85d8f672e27ade6dd0afcfa6eb56f6181729cbd29af3e38b3b4ecc8a4ed724b723a3ad15890a0520a9cc26af542c1092d15000e9f050f8ace54f722fca9b0fae98c4af61f623dfa3b6547dd7c7f269e4106d91c1fb7362b98c1d36f73195116ba3d41cc2bb48e150c70aeb89257d4830e1c270cb936c127a23dc0c6fc7f62f8d6e4417046d79ade659637b6ffeba9b4cff1906a2088e5f6a9609aa704f9a1165b54e18ce8e325c8671f0a81f9263ecdc7efebbf9b86dd008d5f5f7cee56e1c021e2fe60ad190defec566a5d0bab3da01faadd8362daf771f33955181342d11e1a24c26b5d76a39cc1cf7536241ecb9ac2b798ded0d845d1a27c4630316c321b3545192fe684d5ac406677e9bcb389409a0bb05d0cada8421c5344a8cdb750575e263afadfccb386f60fd58489d0bb4370f58f4dc12936a0c1f7d94e2faa22b29adb0e175cc473c2b2b52586dcd28eeb8a244cc8cb76d5f6fa9ce3842a7f61c3e506b19db42f1e4cd8a4b05faf174375a49a3345d08b0c6544bc814d411b21b8e0930d8cbcd0a0b12d146fc1706a84570251f07d63229e6b54cedfe841b371927bb245fea1098b2d4893abe28bf9cf892f53e29787044c915920868935d1005f86006eccbb4c753a380a3f46ac1467db33834c5acdff2c04978e0457583532f0610d3970d4a9b5b3838447839d1fff0506f28421b675effc4974c3bb02bd22bcd664be35d9e5cdc1ec748617d7ffce93e68f835aaec582277ee1beabf7abe2c63fc73c84ea149425c4ae255b6ef597aa1daeb19f6d5a9aec78ca0264ed51417192e91d71dd8c4b1687cff223ea19889910882de0c4f273b29045dade333512c37500afac03dadf9007c95e3ebe1eb027df638d698a535a9ad67172499858e2032af8d2c6452d9b4a0f925e285a6d03775ad1ef89ec446adfb664f078fac8222af34ebfef8c8e5c5d07332510d9be767a2a8fd3f63fc719180097b492a444a1a6f28902cbbdd05dbf2624db029395faade8c1648f360c5bcf6f00969509f4e2fd5532d79e87172cce7840ae5b302068c4e799bff095a94ee7ae5461e52bcd22078a08923c52e18c127deb635785ef81c67e7244e64c1c377396736dffed3ec1692cf5b8c4f2ea3fbe22fe38682a641486f2c458d951ea9d5fc55680cedd00803fadd8ff3aa101e11c264bdfa027ca68801f1f4c873b99f1f3b4a40abc376a800d97f88e29c845d8d9046509c88f955f9ca06da4815e00533890681dc0fe6733020b784d02b21d43155fdd5073bbcaf3e1566bcc390a9ba9b063062a48a9f3f2a5d3bf4bf23bee163709fde91c96de0c5a68fe89f2efdc3ac38fcb7c1db40c61a51a42e9804b0b1dd73cc84648a2281fb4fef0cac1feb14770e9b801ee189241e0fad6797d640cd9cf2aa1f55f4907794b6a3e4090a0074f1649e835d0f72352f14bb17efa94ebba89f2cc195d19c0a4482ec507bdd852b55c79e9d8c59b6bf0ada7730dae0434db913ece0a06828895b07f39a3e6ede7e9b2c0e1466188e1313c67ac6c4f1f57e5f5557f09fc8aa8d80995ef0505ab76c174d775f52d340d313f9b536af11d44cf8cab2f740329ac458c884671dcaadc9f91629eb57fa5e220f89ead7fa2e99f3aec56bd785433322b26e444bbf3b9ac16d3fbdbeb5caf9881cfe4342d8f74ddfc2b008e5e7f36a2441b8f69eee1c4b9b5920e6cbc1adf92f2d06bee425899d3e1103d87dcb7d890b87cab8fd75dba3d58da32f6b43f6b72a1c68b90a5619cc74439f23f6bfeda6e7a8bdcea0174649192ac8a3077f36e7212f0ef21644f05fbb965313c6b59f1a3f0e5c1eb66a2e4d73fe198551733e78562bb24ecff59b56bdfd4c04ca5ffb49bcf1d8eb8f47561c76454dd44b47618b57011a491c2c7b0c8f582b70a6dbd8e973244bfb585aeef41986969bc766b04dad2b9029f14ccddffdfc94c9c51aec7d3098e666ae5a374071a1d0ebd51e1a2b0a105b7ab3c6ad17aea375f660802b5fa962f9d7c641ade8f21d8419cfe672ca5e2881b2d7d1561136e96ca186e48709f2a388aacabe3793580b722c8d112a0bfd1d0c0af75def97d5d4c73a5cc62be153242459dfb1acf02bdebf66775fb3c52326917db73597a5134cfe5c0aff32db8eb8e8800ff2a98b25d2693445b93b94201097455751c08f78b7f961ab6bb7b72286b293c77d219585c66f12c49921c0e90e4878e1ff88252d4728c52613de28c758fa1fbd8fe1c563055120ac8565f85f0312a3c57ce1e84512a7e6f01d38bb3649eea2b549a89f4c7c9e8d048c6cc5d62fc8399a0a6f85477d1a11355dfac6630d01abdf9ae7612169430e0fa9d77c2118752bb4abc4768d0e78c8c1b258206011ff5114d300e5f9cfad20895f4ecc9a0625adf0a0ed53181adee44f47cdd416e49d035feff88a30d11e73e404098a0b2825d765e0c53f5ed8b6b0050ab489e5524616bd7067728ea8ef25bf42ac3f020e4f9b1e6deaf12a3336ece22c90458e376cc588ec99ad2985536bd510ca9f600f856a8db2170bf53339701ca57eeb9ba5bbbfcb45d3345eac67b15a9a7d845716df6841e09c71346046a67fcb5b51be9129c2d0faea4eea5f7af7fcacdddc530aef1e9d1981bd9a78af2a0d502bddd3748095d619210cf3aa8705c8492e156311474629244272eba57adaa7e846c3fd8fbbaac71b25bc3feccfa94e205e8fdb02e93809f7de44c8910070ecb3ab1dc6e088dfd54655f84063e4ad7caa07870e257f213cb9ba4e34e721d51d8bbec887896bb40502d0f4646bda6254059093df14e661cdf5d9fa1a86e2f31eeaedae100990227762b9f2d8ec60afecd697a2d02268e342042b0d69d47305455a40599e01a483736fc1d9becf554c695b07a27a27bf096c3282aad2d900c6b7a8fad55643307493f8f5bdad4c3f8d723a97c8a701fb4e51bc063eb1d8bc47ce98ad16ce5b5ffd15b5f7074ed14e9861552e994912401846c12d1ff7905485680f6a0a49cbf4e970e5e141863a92f64c2bfbfd1a1174f603d2a3e484cffbec5010c3525dc2c6c8defe0c84bf8fe3cfeaf297ea30291d98fa5c334b60f5b157e576a25f1bc5ffadd50da6da10933815c8c19605ed2cb08b7a1e4fa23741de312de8b96e8dc72c45770535a0225522978f46cf1ed21ab5f36e2809661362bd7d96bf9a687bc852ee38c59529e06742092694ee965306f7f63faed26003a1b3a0722a267a68dd9d8336bbb86153a483b6be5e6d09c82d52f1ca592a4fcfc91aa516c7b437d24fc0cc47ddd3c783900840ffb419c37d28cfc87e9bf208439bcba9dc1b3d8f3a8742bd2d40dfff4c47a402f93cb70ee62ac421452d7f50b41ecedadf39bfb01afac4290ccdb2c506570a3bb837c133ee2b7bd5cdd4f38fa55aa486fffa202bcf0bfa43707f66d8d6dce1da6e9f42f0440e81894e21855aef5b8a1099064194de5d0497407309360e77ab62298cc6a7a7171617950855fa6e0cc6d3b58d131d266a956d1487a5e73347d133f3edcd09f0298a0c490d03d5fdf00ba83fe139d3b3c0eeefa89e3be6978224b59a8a76751d077d4aadc1d2c3b7b8b03ba7c0d41e798665b99587a1349585699f793e882c17151d330f5a5bff59500819e8e6940a17986979f5806dd2f482ab65d5535c42510937a1e876afbf69f5a829709008a489b9424cc64a5056a6aa6940a88b8bfb1b0022da31d46a05fa6e2fe2f0ca37b71ef3294c374da7f869f5c5195a2047f2f52c1606a44856951fc2029a1c1144932a1a8347c3178f7353972d05d6dc04937816a787ee7a67834a972e8c73651955d9c2820d0941b2b9703b7517ab89cddbca15c281d195c64d5968e0b04f50d10f46714a669d7b5b44a498bd3444c78678d219ceda4cea398aa096457e1a8c148cf86c655ca7e1701d895f3a6c35b9e43c95305b3ef3d50f8b2ccb50a77f64ccad9105908ebb80a1b8a2f6a4cd61fe25c453133f46da139b495892c89059549bfbf6ebf55afb5a56936f9e0c4deebeabe56f1cef15bd45d2c8c4abde84c0c5335bf56f1f298106b3b43a002acf52f1bd330825014e510e11718c2925f353bcd7f7e8b2b09b57e4e14ff26b88b6fd0a94dbf308f688b7fe963cbb6669ce06450fba3bcacfe8922a30dfb5c4c43ed85062969f155d0fcf9eb57301a20fd246a091ddd91e58f93c527184325ebf94c262fa2110053485c49ae2de63239543f5660d218f531efaeb5619bacaf6ec1a531a6f3e4d2956f3fba0a2564c97acac533b19e50c7f1ee04e0c99171bb2dd2e62c3ff384f10b21e392f556dec389c7004e57f81976a8638172ccef2a2579de9a2dfefcb670f06a188216fd3507683b4225cabb350fb4debdd0955fddf0af3edb592f89d5321f89e21848a664a152a2f10cd30e5c3062a73f01fac6d93b18df54a98507d7b24e1a17e79d5cd7d7a04b6cf687f64a54f769ee6c3e63ea939943ba3ad71ba7204d2d142645247e71ba82abd6d8417e3e2af10c1744f09afbdb83348b8df388005fa197a28bfb1f8888fa91ac2f37e7c50740639428decaffd29f3d04c45736f22ba59f6cc8c9b9312bdaaf2ad0eca04c6a8ba9e666243cefb2349cc162922e90e82555416ed28ccdf080cd47acdab87e3d04fe234f5b61e56a14cfea2ac4d82142458d38c7226bb448488b603508251114422bdf608ade285070d3fb8fa19be1efadb5e98dba8fe4a0c9bdec550f90ab2bb34ed5fe5ad599d3edf27d5a35dae2aebfb3d296b07cb8f99c4a1c206b0b6b59271893eb9683fe948e6d393664831038ade85996477320edd744404e4bb9f874a5c13b49d2f690a5decca2551ace314736f8cc59be9a8275245c14730b58bfb696f7b903f798b87a17aadff25ca8217d71f6dc06f7292dc614651a3a12e26d678f12bb20f594e36d8bfd1eb61e6562221acc3c70b28ef652043eaeac624d8d1680fba70ef5f8d6e90125087ac8520075b5dec10f13ecbe75a01ced0777297615fba720f5bdf4acf77fbb61fdf6f85281b7cb7e98388ffccb8455c128f6c741d1371e4eb77ff31254000c5c7b986ec23cfda8e2c9d14b82ce9c8786199b8fd5803c96bca8cda0c50d51a31f971ad900d6fc92e57b5c54cb1628a4ad1caa3a5d00dbe25028a0dbd729902088213881229d5a5053a0191ca849866e654f9bd5ba96aca03f1f4124ce3d62b30f4e7998627f297d970ac0b1b53d1d6f6cb845e9c5ad21900150d743eeda160d5ffaed2ff2d85c43efe8d4a31c58ab21732c303232c3878801a379941be034b86223be51713a14bc8f59b8f103095c109e1ffd7adfc4e6dd8f8b6a6aaef1533c482e984b3267ab716b1185ff128ceb78a0d6c98cab05593591c276d0a646c2e4e0d1df6232b8e4a6ac844c7e6eedd02cf16525b9cf067ab4297d849911d9fb588bda630212f042499036156d5eca74d67ff132476867da3277ae180ae38530a23e2ec291a03046c5c50343b4168477e8b03995fd4b52921f85905314d69292b0dd5bacf3f1dd8a3d3211d6021a975cbe496fbbb92db2d214ccd233f12af8bfaf2593c5475177e16836f7ef420d8dde0217b20aaa70b79fa8a173f7ec8cffc3f3d741c6ec11a097061ca3fb6798ec89e5465d56f9f6c56a0f34884f5fdbf17bb467fc428482c033080d26b788806d4be674826f6cb45272eecd195719cbfd29456602e931404d8e783245ccaf9ffbc85e89bcb0a960ef799adfd5306f5a11ff98d386c580b2104a6cb3d665f49103cd770f2953ba77ab813c925e1d3d1befa01320f989200", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000088faa138749b3265e00000000000000000000000000000000000000000000000ab39d7744ee99e93800000000000000000000000000000000000000000000000b58e3fd596adfb3f900000000000000000000000000000000000000000000000000021543697d80ac00000000000000000000000000000000000000000000000303b482f3002f24d800000000000000000000000000000000000000000000000b85b3e53031c19580000000000000000000000000000000000000000000000003e4b73366064f452100000000000000000000000000000000000000000000000000005aceab8040e500000000000000000000000000000000000000000000000f5dc05a7bfc973e8e00000000000000000000000000000000000000000000000a5d05f4eb90bb6a3a0000000000000000000000000000000000000000000000060872b59764066d290000000000000000000000000000000000000000000000000001421a9133d75e00000000000000000000000000000000000000000000000f4780863c86fff52f0000000000000000000000000000000000000000000000023927881880d93150000000000000000000000000000000000000000000000000dbbcf61dcefb015b0000000000000000000000000000000000000000000000000000d0ae97d8719c1b55ed60d28990fd534c598c83f2d728a5ecfb56bd465a081d6618cec0292cbd237512e8cbd2be838836717f3a7d3b973f5b990c5a7f1ec421ace3f47a651c5d00fb5ff8417a44f464db9e3db570a7873bd28a5dc9a6bd7a85067e4836b68ce825c611f8f6ba443658991339a5094e904964bb9860defcc05dc2482f953fb6061c687a6373bdb5019e78346a3d8b4aa6358b4b56c32b61afb53bafbf7c51ff7415b3a062e79c6977d6d8564fc067ebddac69d8e8a80bc352294d758b31d13e6c168803f0edf6007b840fb864938af01a0d54ed66c99dd832480f57cbf93354d92dbe31fd406fba859e22d0b8c21ee8ad4357f8fcf75b97fad9e3616fce00d82425ab8ea7cc24b56a26eff6f6ca0f8f37eed160f45b06fb8a36fce6ad6d74bba4190b1a98f0cf95221281d936efbdbf97e1b85c57587a3c202109bd02b0ccac1c0865a52812ff25c6ced7a77eb02dc8b801760bceb81b5dd81497fa55c232c7492bc6317bcff50690bc531833b8f056eb38a2cd70a6593954e78d9f2a1cdfa67b0b906a32ff83bd1e5d632f37b869871b08381ce92e74ca749c5fddb7ae40ac0a0f51a01d2b5f7a67480c57b125fa84f186fe9f640b7461b9a0e35abb212f49cd209f27b1113fe34b0798d1c95cd6e28ebae33697debef7f295d707aefb198d9a0bc0e4b323afe706a0149918f17bc9ffefcb1523a0debc9a18cda5beba59ceca0ef0267a1b20842c607949f2257f3d70b75f00faf629ddcbdf935d0d0eda232b01ce76a7013707c66fbf745546f19d041fea89ec58d214f41dde90912477de87296d3bc7695f2a1b16e258c9525cabca394907434b9d2541d5b4ba692d0bb486213c2e5d7a4361343f0b069971fa22f76f36817ce137b60f0b5b4f0d1be40ce60e54f03a0b00effca458b4623092ca8a33bf27a0b8e9772316d687261523011c1e8187225b6ff80a03bfb97eabf1a873051ed965f503149a705b147d3007de670c317fcd6a7a516bd50c772891e2bb3315f6c2a87c3cb1119cddc826f543abc71957b0eac5663718ae339d51af5256268d9a0474a403a6ba3fc81d52d89f71bd2f831f8944aa85d6d3677e3cc4876aac91778000ccf21aa14ba977af5a4d10da02981669450d55161ca8f053a871d81b5106abdd77663f1ef78bbb0eea21e2a20aaa868967a5907ad5582638c573682a851040e436f2e6c9733ee4896a86784b2ca0f330604ba7a3c5b0b746ee42632eb4cf982d4f3c36ec1bdd34c8bdba023b0d20997d570bb040aab67abf15b560b7c04c52513aee47c6646503836ed35d6f16388eeeeeed374906d0dab5fd1dbf8e291cbec2a6927e9bc4c052c66a9870730b20ff69314b4f45b36eae7896c08e5a06e691c8449e48dedf97ddcd3d674f0b0887ebb0c7f788fa577d949c4de8991658cb0f8dc1af2af7f58aba794610ac002f87749f9d104ae27ff60d06695572ea506326c7e6bee213de54288e8bcae89c0e5751fde4c6228122ad8ff186fdf8bb495f2afbeb85a95ee5cec68db708475d117aca0d4cc27198e0fd43ab5d1f9d7ab9de2f1a36e9bf112589daff912cdc442ee6e8aec7409a15a5fe45e5c201e3a052f12e53d1a0e1ce8ad6607c91f8e9c503c1f9c286c2dde7f8d41abc15a6ebe3825163ede6981ccd6fc9b03112ec426a1fbf2b27f200f4adddcbf200880be7f5a73d27cd24d09e4c6e4d111fac582576079d882aecc30f178bcaa94f8eb567064c9ec9ecfa99034620d0406025400b7a25dc35566bb590e2b378dbaaaa71fcb2279e5b11c15b1a9c8b286b0d1e7d3d611a84b62c97b13a07825b337b9b57aaeef4f86253f4deb5674dcc7a909b0ba5a62bf59aa6a4c18e6a975776383169d14695d6b9a3d305e9a21e745b2b2839515c0daabcad6837b062b7d01dc1ba60505955ecb38d334a523d9b1dbb7ebcc565300e8ad930df265a9e0f95e724b5f713967a976ad3ff3430d23786c016722106840bc0c54b8ab48d4f17c802901bd97dceffa70035d35e38d5ff183830fe90caae070aad78800dfd19c788ef024bd49c9ea8160ac550d117d074afad668a4f6bd510ef8a176328fd8d356b31ca7ba711e3afe130b93dde15d246a37c1270b6e92202ddd3604ceeb469bf93dde8652ce468c184857e67d38139cd669a089aa6861e065c283d1803de3005a23494532da0146ed64b85e40b2de3b63ce5844b29b1d404103d4242b9372da3ab04383a671bf91156329c8325f09bca871eb9b2525bde1e641d847edfcd96cc86cdf5b50944b2faf2e9117ed17de6b5ddc68fcffd88c81a07df6735796c3d20ffd3a78ec7882bee8143ef052475edef9bed6517e3b52428923971ec747cf4f5b9b176d23a4ed0119f7a43044e08f766d367b5cd9ef5610ab73ad97dc6bf4d90f55efa1219facdcc830df23431f5629b286602d4ae5ef903199317dd1e0549502cb2eae16f20916a284e792e268e3b7a57b556ea3a42e9267e8565bbaf76819760ea487f2b6442945c0343be29ae94406f3bfc0df8fddc08ac82ba23e9335dc9f9747a73ad89f80083c64e4e0ca3c3ec5bac16c5c4b98829528096d11677e5b2add86d8d3cb2ab4820a27e6b200c99cb89696853ef6f65194da3ed8a69e1e02e7c956f04f25d64f6d43196af74edbcb57c753c75bad26b046355828fe4b56d8e7b094de4c5284acfb686fca6769822013917b3bf609de92f0cd0835181c4e1e9c37cde2a31e9b44c31b5b6404a6c21cb03c5e2ba6abe4e240fb2bfd29aee712736279e486f8a437aff7e469e85bd5897caad3b28a75e0f2577163a579747d38a674aee66657777f9eae6deb89fa31e82ff1f2694aa389b0d32280e5e10ca4652b0592d2f88f3f966aff1518c47d4f7c9de34cd5193199900117b78694a8c54fce6ee189a22d323e097b90b66f4e2f8012977f5ca861f02178255d84c899b963c3307fb1e41bf1f9200de68ac04bd3042eef9907817e6771daf9d3a34117c81a9c27021cfc836938d33259c9d272ad7d1e7a273b7b54c0a152b720fa2026885f1f12bf2656c147cd9c54fe4b76f6cda531400c8178ca9f2013c91ebee44600dfa4fafb44468f6b425e50c634a024094f8cca64359515839300d925b49ff62a8510bdfed6de3f229654068ce3d956f0f4773106f38d8d40908648c87ccd1c64a2d1e621164c253ff9472ccd8c804a718b313952b120e2488267507664201bc55514d572a0c99e79068801972bc67b062ffaf6db639d590000bc5b218af736380dfb85df08d376733e5f09f473bb3cf82902a0b1070756502076279e5874329df3255ecafb485a0df06e08147fe1f3f85ba6070df13f7552e109ce9a119487805b843c229b96ec320193763785454fef9c984ead471d8a0400a44f3eff7fced4690b673718372c28a8b6d6924d2d0ffcb7cd85d77ae1d7e542da333e9ebc4a6d3d8e4a18172c4a282c170024b8be7a889a57fcc75c1486d7d1b942c027af9c7f98beec95bae62e76068b236387c0f10d19c4afee1274960cc29b78f66787e9624cc182ffe8553674830347e79f7beb68a8b33debca0791f242dd0ad1bdea9a27eb052b4a873a88e3bd0f3be38d6447938527ae035114eec551dda82ab01c2c8a87c78f8441448d17832161b30824d95a71b06fd78b051c04f1be9ad79e03364982723397fc744222f42a3f9e50a53f14d94cfe4decd554040033d5be4a7665044745238a59ac3bf71264854501f0ef4ee5f670690d814881c259967a6df24dc395e0ec1c7b9df807e2280a57705836b38ddc025f3ce8ce6b22c59fbff66af05881bada0c7b1db9a5ba9b601b487b49a9fa48678e51b413b471c1e72a1591e1277aeddbc21c9e887e8c96997880d93b2865a610c04f470dd771e8dd57f4d99b31007bde7601a1873837f2a88c0dd1b886c1ed9b7b411393e1800753e85451a0a3669c9f9a909035e6455bcf5d67a1bdfc4aa243a17c16d6ee6100c493ceea0130062e564d26ad2584491a0a7e215674f3ce0aa5b68f3e3f80f02fc04703bd2c4ef3f812bf23717525e2dca862c640765dd9cbb0a897166ede12c52f69ad514c2b963166d8a0c16223a50170f085c3cb1213897891a2a4d0d6f19ba1a0b8e7faded21a7be15188a512a1e6dd25c9bcc6e6a31ef31cad5c0cb8e1932d820675400dd30942d518df2005ae322f8f3190392739e0b2827b725ec9c2d6738d204989cd547ba1cb3bd37ababc597f124283ddc47a82fe8a4fa89c181082cbc4ac86b67bf74625314994d870426a846c6024026b841efb1b1758390e713f1ca562c70d4385dc5f424727419fbfa3c6632ce96cddef10b998450a8c6f601c4418da4d9e1e56c4eb86c8fff01aec4955f1ddd3fad61f249b6e1a74e0520226c7f49df1282612cfbaf389d6f9081269abad88c9915f8329c6f7f10cd30fe22326f40b9c864dff34ed6c32d8934e1d7a4f42a2cfefb8680b1ec3ce5c91aa90004db4643e5d9b5333344b28816904858056b89e854b880fe2ab9c421bbe47d2c8583c3567a93087fb8a72181db348cfc076505940124d9c48723976a84af7a24568430053eee947b3d1d420d1d3f63ff8e9d42e558ced36e2e5d4c70c47d9b28528cb14379044613b775464e86f94f62e29058c9869322648535ee9bcf8f0b12b1ea3acc204f519f48096e19da14a408266e0c75a2f25a47aff402f8205a562df0af8e6b8ccedb6f16128fb2d577d884101b679c954fa42f4ac6715205efac01ae7bd1f024c6132107293a1553365471c18c03ed9bbb3efaba7a9cdcf4afd7116da94d4d486381dae6a51c4554f36a25179d759f998fe4827fca97fdafcccb17f7c3b9b29db30c16573e4df5197879a1251d3a20015accb4d22271892b67442848049adca18dcbabff5331e409d702b9f4ce5dc8fc6efd52d0205a4516d9b40c64c134bcc23d7627d66edf1d4cd52ae985c7acc213a6686aa7fb98a4cdf9cf2d51b9e05b1ddd31dfc6697cc75ccd917f08a92c8ca8c5faf475f0a188ede9370adb00419489e89e97aba84aa6c77d951f624f9aa99955d56625553c3e7a14ac2f1a9afa3f34f815507ead2a646c9dd5a23fb9fad905ed087cd01ecafe4c903c22aff103f2a3ca614f98f8542b65547bf55a39abd637ebde2b6e437a98f54fb517ce908abac19a16771f8e4ba0887e1b3597b168235be0d6c60d3317f9f792e60863b4060fca1d569252f592e4be383d4a6e86310499886aba36263aaa661f701666c210f6b18ba1d8923a713b639129aa452867e0a8cdc1ae0018714ac7e5890ce6651f7545b2d02d5786a59352cf669926d36a6e3f754a84edfd343d7f88dd270c86f6a7c997e90366731500e3fb282d8c87e1c14443fe2ec4443c7efcc51f1a7f9bf1ffa1be301c4fc73d6bbf6a01b5340fa3d004fde4da36228cc8018b8e27eacdb17bbdd7264a4afc92d63c6bfe9efde14b06ea85a69d6b3100ad8021fe0775f8e3026bdc4607fb045cb447f79eac7d124c3c42a094f9f606e54b2d35f52f1c9001bb4bd8ababc8b16501d01ad354ecc264cab305e988705243fb7966552bbdaf95b7047e6266edc6b12e49e88ef2ff364122afeae596f9ce91bd35073214211c436997a4a33d6fef5e70337e672d8381a93f5b4ea1d06c51c21cf2096e1c41634d00c90b794087fb215c836e4a64055cbb9d50de11ecfd6a1c3060663b2b2e216d082dce2579c3994ead2afe49907335ddc49d4fa813729246dce975da1681aa5fde9249e2c1c140de81649f13dbb6e19ae3a9276ea9d0a08e1750309b0b5fb997116c1e20f25234b03c264b71c4a071bbe93b6d0a69e96b3b8edcea6018758a2c7c350a89e5428f07ef709b5bbd06df80ae113dbde9e61c9dbc99f9022595848e4f0adc98fb3c5d9e954a6ebb28eea6929974874e43277ed5837eac650cb979f976b878063d0221e6cb4822bbe42daedf1076495c945aa7144ab839c9010f5803e219631b6d3ec25d9b21c36d1c2a68efb4d8988bc82e44666690f1e8141f3fe11884f2331e475ba32ab2828b967bb83b575028e4d557953ee3b31ddc20cfccfd83d09af4f819ec755b026d64700d8fbff93d76356cf1aceaca758b3c0938ec673153913cd1287aa0ddb0f8c55cebcb8b6bd95e693e32697fd45f0d7f1d346b773dbfa16ba8917577a6db93867c5dfe647fc231a750f07426e89c4f3d200eaf6e0111401ed6742c6aad55fa225545fd321c94f5edc7feb241fe50fae82fb8cc5f9868d4c5c036e2cda9da4e93057ad89a8b90102b0c9ea0ac2eb9d5501113836c665cca9810f9dca35f399ab192dcbb0e7519c8e948014873cf2ca61705d8ff4fe49045287dbabf5332675a57e14ba2810f3e537644190b9fe8dd2d3120d5b7f4fa873c1b794cf3eb623d39e3a81d8f20115bc06dc33bb3eb7e353f242815891e9dcc9c791ffc59e065f8644e2de3f017d563a9b890905bebe871ff65059ecbf4d0d89724072e1f589411113ccc8d23ca44a1fb4025fc2e668a46b8ee14499b147e6396c868861d8005d999377cbda38057848aff4f8cfc8e8dede9dc28fb01a73e4dfde3e362611ddaec86726e945e330c5c20a151ea891fe74467b60d80010d0eb3028bfeee573db93dc2df313c89c1e545cee7af941eda5eb8d76b0d7aa7561c776b8398ea77ea3611f65571284075419c9775b05ae1c25cc9c5270c3f7757b225fbd1bbe5a0d8a96f0a5712ddbf0e5b143e1d4792317599f42bad0316fd73d3a81d365df0237c745529a2dcedc0d7b8f02501807d36936d8a54d42f1757d6ddfd411af39155b234e2ca522cd89a0759d0c9b3009c21e43401e27e22b3bcdfac5a4bf8ee81b1a1089eb4a643126ba01e0a5ee69879e50293f1a85317111cc292cbbaec3354d0863b0707b0cf8da5ca077515b36f5f38e87e4fd70f0c4ab1f4726a7cbe45ef9f9c657aabba65cf75f300f8b159ba9445537e997d1c0a38797ccf19233d14cc678b564aaa62ba7d6fe01172087176fad6c022a03b790b771c9fdabe10966cab2c1f84d6306d5cf48bb0347d80648abb1cbe78018d9c23d23b20d882438917282c6f58e00ad0bdeeddcaa14e7d16afb4a43c54369156120df55e8d76925bc74e69051b0921dc7b0d875c31f39c25c4810020ee8e84c415772d10884a64cd58b1124838dc77ea531ff6a4713ff7df0a33ca14a524a1f526e18a39af4a43224fc7a5963aa752dfa3bc5404e3618c253c596d477babc8da2ba0faa41b3133f67892399ee860d6d5e8d9ee061e39abade8d637732d85f21517083241064b003dbc1841770c5d0bcd04fb2503548694c9d97633514ea2edf6105ca39c7d4b0f3d36fd793044be89b2fa706d58efa0bcb573cf9abe4d11f83f0c183013b5811ae5062ca3bc440b364998342c465f1668d4bbfaf12aa6f410392980d6dd86acf51dea00cba982be6df81d2b8b4cdadd98c3bfb9a91a771744250e604c7d24c359ab15538ae8245cda8ca13e343beb9a85573cd96828039985b30d246e7a5d651c10d38d4347fd971cd456cbc9e78092ebafe7df820a530f3a5c287b615013d1c779c150c58c5e31c09fac48cc02f8efab3734f8a832af325784074698dd42f5b194bd08879c34a6df579956489206b6164519d972288334d47611ea33bb49bc107f82c2345721e796f87f53d326e1ac09c4f18fcab1713ab76d0c7144690b06cb2dc939f5211d1d37a8ceca6f6525adf710fe16c845a4fc7257281b842c19810912bbb1181a95664dcf9cdba87933d18a5048fcfae7664ac7931fb028726b00de1f6167b66edf4b3f8c745d12ab05fdaba8ca21ca7af9da2ec00659d049dde97aa49977a67c603f4c2bab7438f0bae36297c1d9aa9e896ccb361a60e0636664c2e48de1a528a045b3a01560b636d654a39e0dbe256b47723048261f4a59ce977e1e501672a0cec140c227e4aa4ede24653dcc45c5e388676a36072c7fb30172fe6f20ba8947997fac2c3696b3ba00a0d5a72ffcd5303b757baa093f434459a7ba88638f93c652f21efecf51ac4bd77fc06754036ced23b37fed22cef64f7cd5bfd1e0dce9c8a5da6d2974277eeb9c58a9dc3b3c6377f306114421d1197fc743dc2702a514ef2005ed4b34827ca67956924d3dcc86b7d9b13d340a2d78116c10b1bf75ddd470444bb558272220216fff391bf80e073927da0f0528c0154b1ead201809b2fbb9e90d21130e0de106672c7ac9b9258ee31b0ed20e1d2a3ad2f14524dc6dbd6519ad7669c43718baeb840b4df0d60b63ef29e6e9dd2a2592a2485b3d08d02c3c8404e44934216e34044a22759b8b3728f223218b5f295780f9cade7602d5ee2f0e082b8496b6ce04ba731f12b75cd0bdf676cf2af817639ccc9cd465e586fafb46aaa741e92e1261e6b717887d3c59b46614c6755012e169cc4747a8ad9b7362db7c3b79961d0866bfbf14d6d6183088e414c4b3f81761d6c503bb919c930b6fd5183919068902f359610f08722729e69e6688f3710f9782a1fcb7c9761bcabdd9797daab86a468f69163c49e59d931f3325cf2313194ef9805a4822a4426e999d895f9be09611877935c4de99732a2e1b995a04bf293c16f50dbe83edf3b65eb4f1ef97f388090f68a459cc0418b59f4aaade9b54115cd4c9cf9c7cb92fe3eaa5773905f71f9d1be656454b9291abb50c854777260b0765017ff0a27eb26860ece0b01d7c55ba4a8c228a82c9c5ca23e13926c5cc1a833344934d57ffd077d9071e17feea84956531aec7e05bb1dab9756ae3e54b2cac7af90c75f6375648c2f8b674bbd9b185e60de61a8a394b2bba945c779e890ca1867869363f0464d34287fa19894f703d397a8d6a458aeca9d7b4e58183d80a8b15dbb1a653c8682eb2f4b5b15a2b902e4a5dd9a7dd0781e66f354b81a10c2c6afc3f1ee3f06e4cd03706ec2acba94d3cb3bf6f922902a175e7e66c7e72d02e8826662e5e9db072f79431dd1512a7a736fa613a54bc5d0f48301bbe05138a07f996452e14dbba87e625c5c707453408d57a90b5e1c00b32d529124643903a1d630414afe6cbb4844f94564047eb9aa90e20e6e6e8e613dde4522a110711ef246c165a05721424b72ad332073bea9cd9fecd0c17399707dcafe6ed9bdc294a037e9338dc66c09b9bbe36985c5b021b32332dd917851e19901e49fe96834d851f697162287487b87c4551915ab4b151622c982acd7b0b9929d96c94229bb39b294f5b3cfb484e00e734c1f1cf554245e8ed206230380af73e79c4db31bfac732de6f61ef04cb748a47dbf487ed550657ff86e49bee29a65b1715a2a7e5d3ab32e1b1433ad80a8dea89f7575572699057956128d7e99718f00c6fa760e017f0d2ab1d11445f18995ddee563f4be7edbc55c00ff911054d40b1a55aadac71d64609546a0bf7e60d23278f4f271af9f104608d828ddf9eb57be964fc18f864767121d36926304da1c1a148a52ed35f5e1a3f6a51829d2e9229d46397235df4a5040cc43d240a26cc55434b407257e196ada5d493a715143f5cdea8530e1d45d1e92c391930f76ad6d19ec4ffe89a0bc977aa9ab20c15ecc1f20258e01a98876cf12347696e548b31dee516049b98cde66dfe19251ba1ff94020dabe324b200f0a7050a238845983e210c9e15e8f3ec7bf2e6f18725e4473eb3dbe7c9d3672183260b6a78bfc93809d22bef74d3565c69fd13b8681d6a11c06d73dff9664c1ebb58254edd9a42b57c50d0e2f7b95fc10c669079664ebd96a6f6b7f4d002a77ed57c04d5df10845ec831a420a1c982975d1a6429b32f76e6317ec8786511386b681e25cb4398b7c549a0c0d9a732eeb5c6764e4f87ac90416fcf22b3af1440b73eeb0bda7a31c39cfd50515ca746a465382099ad154f57486ff330476565c51fdaec116cd06f12135c0e45eb7e09b570a61f018e005b23cf44e6ff57866c9c8b41fc0345299c562c4736f442ae214374c27d57b305127384f6246e3e77da25cc699f1154465412f4241252c9de6a83d1d3d3dcccf0698509119b402e4ebc99549d431000adf755ab7bc2e91eab518f9a869cf90437100a41efea719c022f1112b0732d92aa49a2e0b61579304f87fd5f2efc52e7c44e7833d768b2235c7a2d8f9d2b29f45ae0e6a7e4310d26798a38a7eb97e32fc9dbcfba1f7e21bf9558f9710ac719295105d1f67e56210f5b92470665106668ef157bdec212cae3e9d3ff5db6380510092ba62e722d8e15b63dfc208f0da6da7b7b8c08f1fdb332457579997dbe1a55656fb14af1665618bd99835f2cebd09fa5fc1e2c6e7b1623306fa3cef8222118a484d042c7303a487143b51e3f0362149b02d18386da67b3f2408a8d54600334f79c6ddff8fe36174a01050d23ab2d4824a4a9d2a83a72e9d7a5913341ef209237b71535b7642d53bd9221acf76ea745cbb20214b6329b07d4ac15e330af12ca05b10c4ee3534cd732724babb196b5fb9d10c6dc91e1ca941bce024206b625cf7d37d944300b328be339a3e68f309e9b49208e5b128530b10c57804c7dbb2f53769ebf98ffa07ec1792feca939be56afa2d3c507f3548896b5c544a36b3f001543e006128d8660f2c0ddbc1449c8abb1df3b43d20f1a5025b9d702735f76085ed8438f7206db012caeaed7393e2f38baca305a21adf1627f34baf1c30f080b6dee62259851650721e019d7343938a92790bdd6254529c0f808458a9faeb22fd7647ee7f88ac42223461145a13beacf792f2c1732413522a19cd8912235b20392ab99fdfcbff0730a4f09266760360248046179586ffc6aac16fee4959ed42fcb2a42eae6708f5321b81cd2a6eba65b4e4bda95d492d29eb4cc91a973b94604aaeef27e8d4380fa4348bfad2a5d04ff0c8741a4c6580e70b68e2ba9e109be048200f2f679401340331225721cb55ae25bc76cee9a2e91ec0fbdc7c52121d619e6e8b6cd9b22dfd385235c5fa74641630665df948bbd625b2392b6ddcae6510e57f9b5571e434d164d58add1191e991fefa22ef88ddb9f5489bbd2384d72ca237a23f5763adca7ab06789342a05c3a6ab31726d9af0bfe9fdcd54c3018e8292f74f83314abc69e0c017972849c43a7befc13a798510bcd209b693392e6c55a28ee660e838515626c4e2508b5f957fe493efeabd2fb47a5d78d933e77f1d36a22d4c6b8aab3c7fb36e74d09e8ba6c523b1d700b66ce264c5b282967c63865e52320f520400b50d0ef2a08222620180d77baee87a88f3277f9f07521a15526a40ffe656543e75472c2ed85b40079ea7f9f5a256305c80af89e1bb4f01c8268d02f7ef74c73700ed2635aad1020b702196334d729bdd030e68a4d8f5a34c32ddd246078d8a5730262a24d821ee291398b6acdadee7435f04c5df0c63e76cae163194fce3f1bc643d6b2fb4343c5690da8dd0b5ed100622e81565ecdc4c087dbce2b40d61120db9c0a71d36d0b2fc435f1da021f09df04f8d36d769bb321ed0cf31e43aed2037954298ef427efa3510b0c3c206caf6547ebe2df41480c737356b706e24fcfa855fa8271a3d0c199b3ce7537b1dc19b9e187bf9751bd1d451ef42c088940be2dc8ac5d8593360a22dc754eb06ebf3c18c15976f7a370bd14131f01127253cc2a46aac7bf0060a1ec47d431a6328fa2954d56da83cee9f06e4668ec07be8bb72ad9814f1be714d239172334b189076d76c73a58eaa5184b686ebe8101a8c697fcf172d2fc00e5649329913813eabae5ecefa0adb5a8dc104ab1618c050924147e8813b32d6b2801294e148358c514b2b833f5e3967fc39e887532c43015a15850353650f0a78495124ffaec054c8adc76dd625e75c044f71200e0d713017ad21288895f478722e00ac38d4996d0a8f7fe171a795d19746ec017ebe31650700f4c76eedf63c9da0a8dfdd4b27fa8a699c93d2a448f7d06d9dc52c4930d050ecffce1b87fe2223283f247434ba2ebf193c7ed32333df5fadcde96619c2039c8f6e34b099aca91b8fa88c3f290d6dd09f106142bedf0ac29c1f8c056d22a6ce85f45df8c5ad17640a9f790bdf9bdd55324c366fd72f6d75ba9bfb0d1be288316eda51c89921428ce04183c87bb0a9244fa80f44f03b973351ad38ab3bd24e709d2c7de57471d47ac0df964339fa5a72efc3da90692468046f3f24ea0f705e2c59642e41084ce3b47ee6790d2b6bd4375231bcf180a0fcae273dfa23ade15de9325fa0b39a74136e0cffba7d78cb5e0bbf463b8e089e5e62de7f99e0dd31291daf49a2a338755fc5f0a1ce1c3a9f1002bae3b98cd20aeb5a8de38b42f412e05e532caec3a035c15f9ea3f8db06561ec62ce0c7c0c12422dea24b92134820cfa206a98dd13a1b0fcb0cea8e22e20a3f3c33ac959d9df686c6b551bb4b10211e79312775da1da6a4b2f100bd9ab53fbefaf3ae09315e7a73f7e1ae3e0090b0fa21c0e42a1df058480618724085e343a58da5e825852cabc240c940f14fddc0e99cac75b8d9a5c6758b55d6e4f9bac10532ebc34d243c09d684e8277b99cb32c1a2d59cdfcca7fcee82b75433c2bf0df101951bb97cb6d631528d1497d3777029f3dc8926abb3302b92c0a8f04eb79b16f945dd114b85fd7a944bab027454b1040c01850b5af972a3a996295ef6d617d4fba03a75390f548f465adca35d0362ad013e1cc6760e9d018c26eb2b9f9c0d6a8bb18bcff4407ebe1b1835b17fc822048485770506df4d49cf421671a025f2e3db015310776b35a82fd2f586b21bf2860ed1373a2eb05749addd1898d4bcde45aaa34d4c94dc73a44a9f5266b9e311864709503a3e58518ce96a2c3e8dfc1e447609ee2cf76b6ca203a495c2b0125151c01ce091cd9e331aea74be7bad8fe0138c94b033e12da7c2c19cc8ec55c99091fb9d4006bf121c31dede198a0fdb9aa08420fb9f46eac4c803db032ec7c7318d16a34197350ede6c0343e321d3e860afc6fcd66ced27d0c13142518f054761d884aed3762b43ecd50a59df25c236e708789b8378409951cf05a14ae2f8c301e86e10e9a2c75dac1a808064a70a783f1bd6bcd5d1762e588332d3db71ad77d2c66895f66e4678a867b72d8fa053c51ebea627870628968d693351d2afffd301784ef0093bad30c1ba4ffdbf325c685fd021d468b962ef6e3834b1b16fc1a8026866eb630df467b433bc5030d03e31d1845fab0e2aad4e93678cae3b237e8751e0f33f6eeb7edfeecb02303f28ce4cb8fac0746f818f7b97f80284d2981894116758388812c470d1c522502beb11a2baa91bd7b9259977e6bb71cf538d0ba1f080cb65252c017f7d915cf72e1305cd9768c9cbc233d7a2cbac65601dd702b9b13f84d42fad54b4918a79b6f9b87317a1c84d61bdc347d2b2a707baa44e4727e00fd30dc95e3ffb7627e96bc745a782d686a6590d708e6b424ad1c38d32d78030685bf5d3407264c14f0f86a8db1bfd48a0ef5cc30565ec4af80f653686ebae92ccdc6a6e56fb0ecb116fe6bdb5e9b5df8def29e4d13ba2b4e1c43df8ac1ea591f708744d587ec77aa0f74434a6c0775234b42d67a303f914b2488df06d3adfb21dde903a21536b62058f8fa4e09577da8dced3d6a86f8c9493b47afb9e521bc2e0377854af2ae69bb1af04d10a8faabdeb54259dcbda8e554725b9f33a07c432a3e24bb51b780207643253c9de986abfbae1d89d751584a90996100e543d1482ce8cf358632f600945281f97aa741f687462fa3a23c1c01cc686030b029ce280ca0d82cea2b1c47193e344a6e4da2f3a256652bc22ed410f68ce3dff1894f7a1ffdbef96a47fa72bd8f73a38a915c2b099e0b4ece88a14bc60433cb8e405b050b234ee3a351f3635b76310c77f8886d3281c1b7a50f3626a2cc0ea25994d76d00d28261d65c0843cdd25b5f51f0f4e5c522c4aa467a5776b22d942e3e1de4601229057f6cbc81db25945b80c9c7762a9417c5e27965c09a3721087a8831cbe90fb7d1586f76ea5e296da6406828b0fb40b3322182ac14691b31ec674b650f030ed585df07aca077101dbf75aeb7d8832888831d9362571d9ddd452025455cf92707127b8374b4f589739517b47f44ed47c06f09bfa08e7d71e37cbbb95c9648300cbe5dc0454b493fc02b0e2babf80ac9ae3db7494f0cf7dc17e1e8ad12586001f75c172eac0f81ab8c4c2a69694dc9ca9442ee08444baf13bc42d3a8a9f1ed258b34cec704010c9cf981c7788d281f40d8992b1a2fd99509346da4e18068df280916a9553dd08cf6afac441b574aa35b74fc70521feb1044e4ff0651536c0f081ef95bf341fff600916e297ca6f383dbab078e848ac0dfdcf5a8857477995105d766a9d21236c4b06ba90d461862696361346329ca90b3a5e4fb045e6e0853277376295c715272fe5a774c7282a90b75068993786cd6fb6f11f671f10502ad01876b03a95d256fbe9d0b2ca9458e061b115c572c4beb67cf15bd0db54a1b9f2998435d14c36ed021240accc567f42705d1d96841da9e4aa166b30be259771a1eaea35bdac859a94e677251349fae7455a5df632c235b0d25e53e019da6ae7100171dab09430d4db4986c2716a8626194be4b67df0d4070e6a007acde0809f9012832959dad3f231e74c2dd4d3197830da3a0e2c48073e5cca2d41b5af904dd19078c02e4f2b4a2d0ab7f080b92fb061fd1cda443d33e9761d04b49b976141015c3d2e7c4e94871a724b8f24df4d4ab2688b3a0615bce95bcc6e919b1919ff3", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba010000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.113354416, "runs": 3, "total_seconds": 0.34006325 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.612437277, "runs": 3, "total_seconds": 1.837311833 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.565879209, "runs": 1, "total_seconds": 0.565879209 }, - { "name": "GenEsiSss", "avg_seconds": 0.126440847, "runs": 3, "total_seconds": 0.379322542 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.230296195, "runs": 3, "total_seconds": 0.690888585 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.51343, "runs": 1, "total_seconds": 8.51343 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 51.1417945, "runs": 1, "total_seconds": 51.1417945 }, - { "name": "ZkDkgAggregation", "avg_seconds": 21.243403209, "runs": 1, "total_seconds": 21.243403209 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.512020374, "runs": 6, "total_seconds": 9.072122249 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 64.910513486, "runs": 3, "total_seconds": 194.731540458 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.19326375, "runs": 1, "total_seconds": 2.19326375 }, - { "name": "ZkPkBfv", "avg_seconds": 0.347720638, "runs": 3, "total_seconds": 1.043161916 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.388650764, "runs": 3, "total_seconds": 4.165952292 }, - { "name": "ZkShareComputation", "avg_seconds": 2.774372228, "runs": 6, "total_seconds": 16.646233373 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.591194588, "runs": 24, "total_seconds": 62.188670125 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.166238347, "runs": 3, "total_seconds": 18.498715042 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.096496916, "runs": 3, "total_seconds": 0.28949075 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.234150691, "runs": 5, "total_seconds": 1.170753457 } - ], - "operation_timings_total_seconds": 394.71199654, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.078788792 }, - { "label": "Committee Setup Completed", "seconds": 20.221637667 }, - { "label": "Committee Finalization Complete", "seconds": 0.006574583 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 314.973630875 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 317.623802666 }, - { "label": "Application CT Gen", "seconds": 0.315554541 }, - { "label": "Running FHE Application", "seconds": 0.003503458 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 81.660311958 }, - { "label": "Entire Test", "seconds": 422.916649625 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000ec9ef698121a5c39000000000000000000000000000000000000000000000000e41f942fce4d490eb00000000000000000000000000000000000000000000000195fb4f10129d1d78000000000000000000000000000000000000000000000000000096d82d22f57a000000000000000000000000000000000000000000000009954ce731edb6dd3c00000000000000000000000000000000000000000000000c3bd0ec9dd55b67f0000000000000000000000000000000000000000000000003728adde765d0e1be00000000000000000000000000000000000000000000000000027b59548a2ec1000000000000000000000000000000000000000000000005347bf3da34ea673100000000000000000000000000000000000000000000000d629407ed2ca5889600000000000000000000000000000000000000000000000d9316749580d8335d0000000000000000000000000000000000000000000000000000451a30b1de5100000000000000000000000000000000000000000000000c16172d7eecd0619500000000000000000000000000000000000000000000000adf01248f5b8c94e200000000000000000000000000000000000000000000000859ad32a233d1424d0000000000000000000000000000000000000000000000000001e8827d8f4dd91d0bf0d7bc3141a4dd134caf21d77bafd25e2801bae69edef7950fe1c8cf0e1c05fd9ce40ef8073d0e9167a762a736f4facc1815f0d8163bfd93cc017eca42fc0fc496da80095322fc75820967dda64c09ac96275567f11e014fe874e35f4c1b1c6dbbd70d14d031ccf593c50dddda968df0f26bcd235d9bfd78d7fadcf5dc4c082c8650c77afa5be438815c3d42ea08aee11084cdfa6acd4d46d109ea7bc42a2042981382bef951d645feb6dc93d46518f03db584013c33209e0cfd8165016716707faef9290e1f2d5456366066051d4a5b1ed85c9b56b2f3a767c8898ae08819fa1d753808dda784dc6ae81116c5984b2e3f7cd8de51a83adb0afa9b3ca6432a1fae1883236c06bc4f084263a4df184700231ae641d20277f6e7626fc026c52dc7e925492285c0754912271f4b382112f5a0cf23229fa89f6b1a66c5caacd114260f3e6d1f10af300e1843b16413df24e32bbad69247f23fbd482469a12cc72b45ef9117ab888d998d42e1d4cdcdd8fadf0f4c0615a748d25a80b35c7b14a408ca6b5c9be88eb9f015340260f7ac3d4b702fa29ea583c12aedde88a29295d507a99c79637f9e6e508b6e2cee68bcf5a4d55b996096e9958ed14b64160422fc272fac98173a2b2fe6c548b7cfec9b9936a29adbdd9d16250226e77c797b945a1d98a9dcf2a7dba90f9326de6dbc89a442570e30f26606652826872b79016e3c26d7da27f6178484f07514cda21d42c6bcf42d2a77bc8b4f36e5f2c6d29d063b120b810cc1c1f2e69ab126edf280494d9a9c94507eec1f37d5b66b71bc5a23e6128dbd86ba4a243a9a0cacf72100f9d43b1e8a3dd25349e062e3b29e4ca4db2f23d46ec0a0321161ea953620e0d0a400bd522610d2bf3a1d1f4e03408280a4eb01f2c96de382a1d4032e1a201afc51eae9795b8f229e48abd893c57eb94f7a512a65956c2aa748f1afb6f0946d8ccb60fc874da7ff480e945a506337c8e046320e2aa705ddc585323194799593e9e5d359fd88a17a9f65187cf0210940e1659a29563411bd539cb6be00d056442c9e842afbbe75f44280307f3666287e0f21662f49976c867a62e4c2c3a24b11ffcc6b043a772418bcff0b68f9946b9f9c73b42fef2ffd192d0fb4e9601d72ebb81253217d473ab13967c8cf0673928c6187201c9b3f5d4aa22cee646950cdcda3da589539df5f1aacb1d2725dc60344ee5d9728abef864a21f93aeb0016e4b9a9b0968fbdab4e601b8f88bfb18de531d9f78019d8b1ab781050c8fcc4062bcf4bce74df6ce2861013af9fca034ca763399e3010091a5bf71607501a0d61e999341708104b83ca21703421c2e400192a1f21bd0c1c673628d372ec09c56d244a04560e265c971cc505e31b30fdce03472507dd0610bf3ab5bbb3bb5d6bdc5ca8cbd82afb058dd7baf634f759379524cf80e7162ab4036dda4b58ad3781247af235afafd6e919f90dbb72ac2226f2f86bc2df1c008f6ff95a49c89d880cb6af606a4ddf7e9ccf097b1c4b946a187f90555d393a11a1d909a57fdfb30a8410180cdb113ccf6fdb0a7fae7281c76e49c0580bc3160d77aa58dbde5ed1d51f402a0fce68badbd10cbe75c128fbcf22cf53e2709eab0834a0e5526c91fbc6b15c1b5bfead10b8e6d0ab3171b2d8a106c3e4fd026a7a01e840edb4e03449356fbcccdffca3a45a59afd472fc980d83fc1504e952a2790013006ba1e5427ac816f25adae5fff4194e25659862be62b0d43b8961bc224f11ccf3bd16f35538162c261e65048d9e4e4cc70286af0eac700570096141331b0be19c23a4117d1bfadafc509612d2d774c13810ecc791068dcda29d75f889001ab891ba8fe14dd2d11f05b422d7484fae0de35e39b6b97451cb4dc12cf5694d0f8de2e3ee7e5aaae8cfe3633efb2d01b1cee79b870603ddf78d339a31238439000a22a5dde7d7abf2e0f72fc8806d086d73f98f3202b454db461aae23c7a8a80b51da3d101f0721ac69e21cf9cade88a606ddc8f8b4f9c0d80f12e5fe750ae6200cded97041e21930cbdad1c0903302950346e6a3861935a0208dcb71ad1c5a0d627447005be70389df3998f482d674f40ce14592c3407f2eb19213c436835406590d2867f21c316ad81b9fcaa64fb2356cc4023c282b8fcfca989770b2ad551a20ce681a2dbcbefa6c08c847e87bc3a307e36cdb61cddeed0f464b27e0fc86290793fbb62b364038dd10ae3b7f13b856e9b0b427cfedd8211248b16e392f4f04b4d3c00630fe829bd6e54d4e1eacce1c37cdd9c6ba7bde51e8ae7591df978729a97138329eee41221d97413dfb35c1178d301098d2972ef49c9d3d37ce8ba92142609dce818d3dc272faef7a9d6739c9a2329b3ae8b741d1cebe2d482868630fced55ac41271ea90925f69eaf5f506bf915a7b60c7b1cea5a8814f3dfb2daa2712c16feeed6b5bd73bb8437f0b33a2b2a09557f97649370eb9c15edffb6cfc0d451a3b62f6a1582ef86137cf115d72346fedcfdd09cf2057ba0ff3d50cb3c5045c72890b5ebd82af87b0a950c802d4f8372fb67d9cba3103e4c3c21b59e887164a23970149af17c8074496fee140c989307fef339b5846f4fff60ea4771b6f1b627b1c3c5e6534abf8e1ceca32fb8d2f7256135e3a2a905a65c7c4900a5eba009da53c08a8264c2d95a8e9e0e66129704ed450139855b738a57f2f8e4838501d88faf34c798100dcceaa6ba31a844742bb57d646b8403f5d9bb8f55f0c3626150d5018b6695de63c4c6346253f531674c1f6083cfdba94c3e7281a0de65c7220681300cc6b94580371624a9a778201db61e5d292c9984c040d8a2bdaf01f6528196094e94b726e83f8527b0860b11ba1cf78a2c1c5c72f85a57958a2689f361e2e867bdd4a28e7ba5be4fae95e5e66d44caabb18509df95c5e8d04060619da0302378d130924320c43337c2b9f71339261401d608ea2acbcd25fcf47f7959001676330b04e5e7df983712fd2ca5159de2b2d2e9dc0a3b1e6dc022fd21daa9b26f45730dd7b23fa1299408d88269521da1225e7e8fc4aac0b0fa30375a218de0d97ca096e738e1c890cb57b3c4fe078a1251f33d24e52695ca0fe31b93c04060eeead6540e060ac7adb14f6719ced88e631fc70ba50f8ca8e5be17c0d8716f924e7bdb0daab87e3b6901720eae49e7ed230828c54c597dbc024797cd024429604a1ce139df61c37b81d6326f880a4077129e81a08a92be8bbb7375805a0b71409c44c049dfb0093e06392154a6742f353159620f2e94f83e3dd9a069a8416fe1376e6fd487a4766af44cef2eab670e550c8fe51df3424de3c3e215f9a7e8a3106073d4c626b7a496930e0e59a8898fe992a41604af683ccb5a29e6e7840a4260c5ffdfb5a3699688ba4808fe5eb9bc44bca0ff5979693272d9a1c362b9843ae28c6fc1f51cd98c5c2da4242c9f650d5c267e513d8208a40df4b7dddd14e789009a8274b38ce0c3c928b4927b9548b283ad304295c0598dfdb0ba0ca3c96b5d8094f587b973d1425a3746abc0daae7480fda4a2bbb2ddabaf50c5770f7291a950edcd9863a06cf513bd75c3616ada207dff99bc316f83d7d1156c2ef24c9e96c11c9337457ea5b6c6baddc66818eee1decd61ffd15b5cb5cb7b5a9eed72a4c5406b832c1d539589d402db21f7b1ccbcd9c5272379ea7438bea5f9d8f2a5e5739057112baf67d6070c3f347e349e0fb06cf579b9a8f289ce45cfe74635d5253980a126858ab7d12980496c06a7b7c6a95e7f6e4ecfa2957c77da9748cefc08d8618875439e9e2168e7e05a1cb42af517def29aab9753a568f465bc9e1378028090e0e94f05fa827a7590b317f23227190c6b01454f6acc256311dcb76b3033fa7064fed92f22742923d2974e504ce93711614bf5c7edc00c0a3b8cc38c014317704e2d1dce3b2a9fafa881c14edc39a36bc545db0887c9475f53a7ef5eb32f90b1e87d8f50fa529f18eaabb7b84ec1868f9eacd9927431091cde1044ffd78afca2563fa2c2917e76b9aeec95bbf2c2da274934c839ff2466e449ef0aabb5a444c296c050c8c44966025cdb1b8fc46bbb208c3a372758c440f4e46069ac0ff2e5c1e9e0bfae61ed536008d9fbfe946f189eb66f0395dd51247f73f0c18167f594b1437ee2fd4a565041f523b89e595c81ae1f54d72f42f538b8dde9b8989aa532b0942d496965da36cb461dae9ddf7110e2440295e1cc589240f54c0007585df172c1e3279e05a2d616620ce42c5e923b8b41fae97c732cf92c1efe28d039d673d1acf1670740612698be6eec0d7794d28c2215f2539f181a4547e7a027cda29682b0271548cda6b0d33b33defcea7fbe8208e11f22fd00409920c98e408dc84911f3be215ee39f6d6cf11eb97f9a1552dea6e333a850eda7a5865e446d39451a91ebe76a8e4991a190efb68b81ed74f6b9fbe5a9440f278e006bf4a050caf74ed29ce83fa8130bba02eb6d8502546641804143dffadaae81a48455c942abaa3f92ea5dd70c89b99cfaf9039646212b32dbc450fc6c155ae7e54e69feafa93bd480278aff364713a492223b70d38acbe5b15791a0391e5a90259d10af8d93ce96a0f07d35efd224a4afea83eacb73c5cb3fc61a66b75551deb76c9db8f207f0e9f1377dd7b8cd97d7411b11fc2152d21d6e04591ca623d4ab2e5f1164d6a95896f2212ecb096f1e21d1492b2469bbe96ffcfdf77502b7338122562cb390c30752f0d1459977331a5b65fa24818be9c9a0b01e5d3652ce97ab8eda935aa40935ddb0d7e334f37a1e1e9a24428e5773c01db1495d15f23b41f95113f3e7cbb2b64b30998b4e26a623cced0c6c246c2421d7a354fe1cf10e574ecdfe20887bdeec0ff2e9f7d479d603603479d950bc5b65cdec41f175c50e12fc2f10d513693da8cba09be3be16b55a0b63a2f6c83cdde050b3e979a19aa88efca5544d356591fed7122e8585a390e81386ec788812fc378251cbcca61186f818d764c5e3ac2ff651e2b1b84df02745bcd24956724035b2fcbb13b09e6641ed6980ac44f5a780fb875286cdec284ea9959449fd7a72aea4d800927d9c1bcaf02e28181b58536990db6030360c87597c8ab02eedae23a1cbff1dd4d17dfd9b3ff44f5bf139b63ce10f51705255ba9ad0425f329897bd4cff19afe6ba0a77107f212b1b00a40861af4df1cdcc6665a294a4d1f020d3ce73fe5094fe5384dcfa10677dbd29fd8669af5e223ebe866f0809bcc298055e9f9dc847dbd9722f94c8e7eede324ec82cda66491026afe9a61d8567e3b887aeebb05593a672d6835b574a4b3ca4d4f3cf5e99f282b55d6f90dd66f2417dcf5b93b85d5f63d7a88bf7e11851e7f811c920eb9833a149fb762c1330b97e0a9dcfa44f1fead5d80dd39c321635bc63534971f52453825ed4c0a060d6a421b5601c8ab40eb8aaddb993a06c759e51841e94547742aa923eaf9bb18f29a544fae4f5349e1df1dfb84399b8b21a746775de962d5d2f0461273e8c8a1b50db92053f02695053619ab0ee4b7d36c46d489b9f6e3c5a4d15f256a827b4c38c9be76860729c4627c31ec725e74ec35b29b6fd3f225793903ff04932416ab27e43368a89ab6f380a76196c1a282e65a5496624a3f8451d1d5392390fa9bb3aa8dc264a0cf6d92f276ec585abd770d5e5a515c20c2e8127dba660403190dbb1cf5a965b95d9cb997676fe763ebc5504b4f33a9e67aab1b3017800b399001da114a5958d9c0997270fa455ca54c44328abb04ad1d38eb1ee5d92e1d848d673315acc09e92178b36c42336cba10b15e42ab30533aee5d6207a23fa06cca99cbe2e1e5ba0a4c48a20df0ba9f7bdc6e4865d24e657de9f8e74772ec323f94d7faadb7609d70896ad03b3873e7312de54ce84a0d25977a186e5d36fdd04826858efc95f0e725c1a0170f90126e28218127098fccc6f8e93bbc5dc94a42d93b194a264619494f32d24c66f7d74266ea517c85acadcf79d983a00c58f9d3021f4345b0d3bb028c12bb97fd98ccd2085891e183adc9b7bcf30d7f7a16b8f128c35858ad8953b3e8c1112282650e531e94f151f3bc9e8bd392184b82919d518584dfd8ec5ccb00e67b92d22da2594b81e7338ea8e17fbc5a366a20b28d6b2157ffc8ec16bb4a79738cc95b88255eaba014f04d4f8ef8a1b3f837c6cc2f26f2deda5477f7f3681eefea8bda62548d5d8aa1ee1283ec16f53e4f3df239a12f403dc3aa82aaaaf4fce71f168a3c9bd12138fef7f2a75bb250a84e5b3dd2b69771a3ec4561abdd4dbcbbf63daeb7ee1fbfaf8a2e14c8e63fdb33e8ca97fbd8dde2c3a64777200326b95cfaca821f536be5e7103037b42e6db4b8468a2518439cf17b05607a124eb2ddf7ea1ef3d0926610263cec35870f64d736cb576bec97ff02e4f1b460d3218f62b5fed7ef4d5734311f0d96370538c1e0d2a3456203a4da1149b20f6f624c1a5eb14f3b6e23dc5703e6a0639c4f4295421fafaf44b43f3a3295139e08f2740ee9928e679c015b16e378e0459daecc0e1289990dc3c43d57d2ce7adf15e252b64728e4eb4474e069291886becc5e3a7f3f7a23971914acab50999b3d1b1247a99b4d476acf564ec63978549733d80e5c89f29ed55df452acd2572934bd1fed33bf7a38dc15eec7a58aa24266f3269d64789f092e7f2226084058d43881ee24895a4ab41d6df42c1fc1adcbd8dd364a4685fa6f785590c98ec2e859091087d624d7953bc8286b9e75152cd225c2ded34a72de786d003f53ed305d0e276b613bf274ddf18d47d8671e1cc36053ab1043b7bedc8c8f73a51eaac1a92867b56d997ada05774fea9de9098df706bd127ae6ead366acae1e70df2100403c7eb2d5b35a5025f1cb28d1d658ce39dca67b38430c42a1f6476135514420ad054a7fb7658417a95ca357ee3371d5243796fcd6ef5d3e756fb3fd388f4561fca53dfc96f682133bb48a7b345c1111cc9af0516b67195b851222a50bd0f9e010c08f4218d579c9e5bf4d34984f3e67b76f59a09f36c0845b5fb7c7de8f516034ed58397993debe05f425882b5356afcd858bd95f36d21d30a8cbe81b5a433006813e0a75c02e31ce51ee8e4bf5e9db6b9bb4da9e8ff7cd7035a2fe9b29c1603574f3c14bf0969e84c55940ce0507d8ff40a14d3a3807c98aa06d20addb2961a35842b5d31175d70914bbac65f30541fd7939d8476d332481bc935db7686b40d10706636fe09879dd9c78b5d966f85346262cec45b9ca953bfce5754a973482191b13b36cdb489638d09c696f9789ca8ce03bd3154563073d0152b5f7938652e7e872589bf6af3ba89d6a643a1d628c88b615b3f9ee38a0415dbdd0fc7f4ae07c69d33bc6e4ff89d9c048f3040bfb2818a0327b6241aa91277ec984a54b52c2c4134c0f6765334c4b9550c24e031cb1a0b08ecca7e8acc43d3da56e06dd3222fbd07388306b44e8ee96acc822500c3bea60f0ec5ddc469b085f040ad7435af19c3b6c8c584c648f81a3c1532af5bfaa4d9f7800b1ac53726a4209ac340d8f00db7bf471cc277d7bdaba3b289cedb5d29e8b1a0de6933389bd97b838b49b3a02d99764a4165f2cfa8a42dfc3eb407ea37372abc442d49513d1a248ca15837d7200c1eeba96c6dcf63aff0c43912249be0de697e8216ec83c7014b3890379a361c60c5cab97ebcd3a91c63be61161d5776fe04a0d4b7794547d58434f22b1d2e01df5c3496479fb742fdb383182a0f82806f034a73d11ddb26c29a113f41bcca286191a6537e9e2465081026bd75f3d8004fd720005839243a73636179bd8eaa16f74b44f5598ebad7ad0ad6edf8518371752effbbb0d9158cde2adfef8aff80028a14d27017e83282e4a58d3aa87aa413c0d1b455efaf476696989d446625c4104a26b0d94c9660c31cc3fc65e3c4df1ece6207b1411b125cb84e3557d226731a7a20b3f7a5882a6ab25d92c8599da8a7d0f6d4e6639b834d5a1ad0369d2f45127cb1c3222a85abae4598e65930dd8d0470cbf40d2041afed6884134142f1d0249afceffd4a23865d3bd073b67004cd5390222d061931e6053e354a0ff761cd258c36a285bc13906e3a4fed7c07298421178922838ec126cf41c69ae2c9b92619ab4f4d85454d3a1194383446a73388cca8df303e88c55dd395632e9fd30d18216f670538dba3684ccfec7bb4635b2a8d22834b0bfa34b5b20bf49939ec1ef717f399c44a938dd4db05855c2468e3575d93d4ef586d759e572930b84e7ee1dc1fb18a2977fca5896ac9a9b11ed5a95856de6b617a0144867fed36d3e7e8ff73043dcc0f89c5417055cc4c742a0077d552bbe0621f5d8149a525eb2950d8c1d720c6f766e5e8939ca092bee411d46b2a87156db048d1466f97ec9418860b4f810ecf93c938c1bb700075d96d0ee33df8c0f1ed946d36339440a75ceeaf5e4877057a7f59931eb2dfed2c25cf8c8c1304104f31fecd29ae0bcd927db54898e4a111ffb3116d03b9acd3afab6184dde59ca077660c0371dfda948a8002dfbd058706933c4b4d9908ad759dc904e51946b76a5867436f005a2d19ce71996a520d6a195103257550171b731843e416bef558014c1695d5088df4724f7066e138d47711c8063c1316481f8d0bb12717fb97bac786a86b1f9a5671c563afe2f130da2d02b8a37417251824e2daa6c59778cec9ebf043455e64c8b0a90b89404b069721049b53b68565413c38334d8317b7e3ca57e03a76bc32da196655c744b65ce208094564dbdf75b7dd1ceed6e687cb6c33e991132225d05961f3b1286294dae60f2b5e916bcb4c5109e9cd706422eb5ae2ae1593dd462be1d3254de039b624537e24d695f9479cc9509dc1abcfd35053bbb1469148ab8b69535b6a24a7d32406ae264e670d49bea15796d45e6cbd3328d629ae50a7cd9e039e3aec8370985a2b1a0398016bf94ed111f901d936a82c85399301e266d506529a4e8cb8a156b512552524763d17dadf27538ba2a18779c5641f2ceef4e00f4acf96b96c3d223dbcf11a67e18fc6ee529f1d0c5ce12bca9dd5f1f8e4c3f9b05f097338fc517e55a08f0a29ef8caf7b0b3d6f8abc1f930a7367a40c58f1af0c9452f8e866c1be94cb1e10b8f25d5b84e601da02555eb3834514a9707ec8d17ce1b9f1fafa25fe8721152e6b294d391e83a358bfd180718df442d2341f7924da88c1f18773266dc326451aa57efabefbd9a5961c1b4bf59fc240a3b2fb88d10c8006e46433dbb521eb6b2af8e7808c4683783411ba2f259273a45714d3e431138dc1565c9a50d639956a080b639dec74c42cbf5a38ca79e6cdb2757e5bcd9fe18a421c48ee9088f87eeb22168ee57508a1a494ae8ce0b902f3d37dc799eaf69da83b70ff3b07aae4ba412502ee2bafa3e54e281a448e024b7a0cedf8b25ce751d0638d046671ffaba8f910955ba929a4aae6df051c7b89a70aefdb331f87191304d438517708a89ee55c276dd93baabb317b3c30e8406fc70f26ea09a18f9c2dc4b527a3171011dda53822ffe71aa39d9c61bb0d01668c7cb0791a26cf5a84a4e764cda8eb47af8ef0641e0407eb0363e9de0ddd095f007b7cfb3641afd06c355972e0c9d2b491a903410cdc1ce070fd0bc607a3e4db6c41bb13f8b2d2328013c608975b32ededbd44ed29b5ed7618cd1be2ce97d9c43e76b88e226cbf4cfb2c51fb6b84218336b8fd4c0d5bb3240dc9d465fe6463f8e0262479813fb30c001ff332babe68b455fb72710009d1841d10bbe3ddbcaaec39838218778cf0c5674d469f8a4efd30adad662f0fe5ae0ddb452d32153500cc27bd4412b66ec45e6fb646f17dd1fc81f7c73b842b4857e3052aee438d2c763e1ebbcbba7b7069bb6d823394f03cfa195a39f18d1658a78504eae6f75cc451f170758d7113541ccb415867d0b58beb96be221048077e7d29008740cb1b48a6def944e0459b21b3e1004004c9e179001389ccad591c603f2ddc61c9656d59c818d56b711e30bd2297566cb37b89a11022e3cdd6f71c229e7447a4c86292624b5b740fcd8dbc73bc1e46a015a4b3f98cdae09ae00b177767121241cb9ac8c79fc93278c12b96bb8e083aa11cb0498a73b1f5d37afe06705224f43e0f7d3b35a77d70b56660856e0d3cc857bfadad79fb9197b3d3cc242055656db132f6f02fda406a393c7dee9114efdce2457f5184cdb24b2005e200a08b50a61efe929580ec871a4c6e67ba64688becde11c210bdf24a631795da19735508bd0e3ca0ee697304516416446a044f7f727064d56488e407f86c1a490eed9a8e618a11a9409d570d687d160070f22b4f932fc86dd65fd5ccb3881d2f01bc09e3a463bf73d783f5213559fb74d00def06547a0012f787e490ef71ccd713ca081f1f774eac7ca1f375d5b8c3770ecb120d3206976c6e5e1d8cf53721fa152d8da9139720432fa6c991d33df3494cb3e28618cb99dd6b393307a243d59205d725f4a775f611ab67eaaf9eceb852924330b7a99b4a858938940ed5caf7be09bf3379a4987cad24280f7ee03cf166da0198732d8471be63261149d23f3c5f1d71e74558ee93e3bd8be503154ff8804309ee04aa82f19edee7d2d5abb503192ac2ae60b489fc6eab39860fbe9fea9b2b9dcd75c580cdda69e19d3a9f06f95115949d534059557fcc3647457dd0a51b99b5830c29713cc3fa6bec10a1cfcfae1da83004079637147a159c63f632eab4893aacde0642cc2d5a082f00c7f51c37210cb6c1e85b9bb7c30b9b6859ad823a2824244bb938d5700a34a82d02dd87300027febe85e66c00ad1f8ae3f4cb8705de7a307935a9b09c5f83b9cd6830182a2d5684ed85666bc9f259d55854b97bc7af5e5b0bab4fcbb50845cdf6120109591e0bb326e7bb1528a02dceafc2b2a35d747e1461c2790654cf1c28bca13446991f833ad980f9d49fe652de455e78b898729071dc77f08cc04e76d289fa594e8711e19a4c38ad676a582ee9143e91a782f326c22f2d1eeb069b7a97718275c67a1fdfd4d009c75184369d892b1c6d514d985221cb5206c7f68d3688562c543c782245aa3c0a6b70079f244538bb89270199b59ddde6820a5cb1af06b39adeaa131a84734c2ebbbfc7ec2fa961f6be227fa23f238bfc7a21b3d80b3a3886b492800cf524c843bb2e4015995a1d2fa9ec624c9d4df6754fbc0a05f474ccd63dc4a0012dd1099334568939b2be801baede610cffa142af1b033817c60d1976f6c15412d70ecc05267712f7c38d569db6ce29c2fa140e469353dfa14874aa9dfc60650e8e7bae67d9f7ba8c84d65bd13a40b609f899edc8feb8b8b1ac1579330a75bf2fe6869bcf2328f5e4e33b94bc6f2beaa92831115b6d467cefc8a4dfcefa31ac12d7f722e74af1cc316f18c08809f55c997be302ec77ed498890fff92f7a94581621fafa36ee7a1d850b6bb85afe296408dcc86c13ab680b9e29deabc49621fc25906893f898794b11682c468b198d9d5bc6ba3337d3b9aeb6c6a5e6745d3a31144d3023681f47ea2d17736cf5cbf1114af0039e9f8c5ab1a643a8b6a109189816a8492490080135728a91d1645c79e51ab15a41f354e0652fec1176cb03430f08623df28499bb77f02e5b1123e82b6756e026a4059fd0d3cf55fd8bfc9deb3210b0423c3a9db9283f97f4d76bfcb8eafb2e108a2e66cdf599b0c8e3c2bf6c6922b0a2b81f915c9d5f0434e366e54d6a4106ea972f53abe8ff8ffe0ad1eba1430cde400af6ebdc11f2e4e2c3d206ecbc19adf11e2adeff7fa31d917b55ea2bf00cb57c0a65f17ebb620679c389f99ade39a20505d0c6808a6cb7a0fd593766df227df8dbfa84c24339f15354eca15dd767e20c29554da57c66b67707105e845b09a7217d628f1340c453cd388b1da17efa05fa276f6e097eafa5a3b74f99cd2e114f6355af8024d3b8ddd9b99d73f8c889ca9c64b8e0ed9d77a69f99fea56a660d686198b2405606c4bcd45e6e727b19beeb31c8b47b0022f7e47c9d8ece2b6f2ec0a6dcb76b443a39b13338920905390616eace86842070b5c9426e93e324e7272ea108f073748438c0af8d393b38edf9d52d21fa46766722c9289692be58062a5ef5ff8e5849bb343ba9a6266e53b7153b1bce64d139cebc408ab095f5757025b74f6c065b749ccf62bc64a21cca2a22d79cbfc59c513e29976922d085470a01274a09f67c7da32d829b46f8a042a53d3eb56350003bb9c5a13400fbd579bb112e3a0b477808a9d8cd63e3e34a09a6c2af7d443f0fbe7e1a3d28602877c5761867c1c0abc4931171e3bac1608f5cbdc9381d0963de0bd82571511d3ddd658e2a9728021432e7cb66eeda6d4fe87b4675e176d187b219dd77ade78b86318f461d20abce28731e07ddcba90a7701997eca0991f165923a36456ef637fd79bbeb05adb1bc23f502c3bfeee6605b6d3138409c87bbc364179ec25da46d7ba11fc92caf8b9a23397d2e46e9a1b86a90c7c99d613f1e3c085c6b83809ace5f1d6d532fd1c522723decda9bec99793be2dc9684a48fb3567cf2605419f85d8f009dd728169b519f260b527b25a970daeeec072cc98fdc23d4d7730578cc6174d7f8c40619800139210dc43a7639768d87a358423cd79442ad129bdb6adf00d0b219b20f9baa3f2d6ea3fe67d1adefef449ee22a10f904f3bc7abacd99005e61c338f80406fe2c1933d5ef2bfe1ee8ed9b1427951d5ce603bee9834b4ccb297e2acb20259a5cf41a686e3587f4f895b59eb0d527a1f95735f7bb29bb9d59c94a48ee403026ee06550afc53fd9fb85a8410a74a7fc03ff4df74982ac8cd2a8d5d1e7c0315f9438d3e31c0e36cb921fafe6e806f6728654181e51db4cfe13f9d36342bc806e818863c7e1e5efc1c5168be37f22fc77ee04a6589a85175f77bbc9c283f332393aa9c6b3dbdef56323f063b8fd6b835f921f5dc403f4722f0829474431ac012c288f568c10edfd731dd5597e1538b90cc7b7ff1fe00b720d0cb5a021ddc470bb7842a2fa779ed68d676dba9ea7a0736f469b4b0e6d232fda6ac2e910168b5293b84640a7cbf59198c750df56fd995b3eaded30053ed3c0154f1785e4cacde1af52eb9d77775b1d329ede55e6ac3c444790ed3cef6b0716efe2114906cc0622618ad8668e123de4801b43702661449265d930b426e51a24f18135a1e5258d429b2a9858a26bb3983fcec97e10769ff4b430d6cdcd35b12d5802199d5ec720727dd5bad0298f5a7ea86723e3a66074c24fb52baa95a92322d9b664f99464c1c104e0de8fe760ddc5b596a42802c6d711e028d4bf57fe361effee928e831f964157caa124877aa387111135ff422bf789f61ae66a3fbe10bb39d5d0d1383320205795fc08bc6f17224289bedb10920ecceabd9f94f3eada029e345836ec25f0d01603105878447d2d67597e5863401eed65d49a187f4aa2fcb510bea0c4cd5bb2e8cc9181a570eec275b55e8b03f856d06395953c5c0bdd93c918738f629e3b90372621f2309f9d913fb456f0c8d20b0d7906f0bf59a8d8e2edd27b7d08be9fd26ef9327e0607d7f1d49c2ae3c4ed6bbf50c656577fd1fc7078821ab2a5324b40de3d2b4e84e33e7978b9ee0aa7495eea94da74589fe1148d56c03f20ea3f2c71329a770903472464d7014f35b3107d1a7d0c8c712265fed9e04431af9683ed5033ffbb3cb1af11674d42845284118d87134424e057506f42cbdd50c9f8ea0fb2936d4297ba69dc7a4353625866a89e1288a75b11af9957d992b2a4e8e11846a25b4e1685f8c89b6e18e05192aad92ff0f8b7959650b7ef7c88435213bfac0c41b3610ca52adfba126d2993368ee3ba12771f382a8db73989bb89b8f0a2afd4e190eb1950b78049808b2cf3bbe30a7a577027e638b64c4a24c610220774cb83924db815cb811bc9d986c4e8635112d135517580b0b3a489184f1d6317697c1f02d5097e64657cf18e301454bf94c1f9fabba3bf2dcc67ca5e1cd0d84c7ce935e1a7bf1fd835d3f46c115d3500b3a71f60bdd42d8aa66e1c43ac536a5f3095e942cfa2e99494fdeb5b586811c476ed6e5b2cd4205c149725592f00c043a7eab8924ec7ef41f85d65c9c07c46f4e95ffa683d2ad865fb6a1da4be35e301d0aef812ca0e15d566bd5366fcd105dd32b7c8abcafa8cb6ad02c158664c34c8cdea2cb2ca0d2b718149bacc1cc8ea8a14a6dacaf52f5e8797659d8f2f4e7028910071f10c993da8089e7ab97984622bb7386c1d4bc50b386e32a10616402dc89ad01dc1cb758476fcdd35cd2fb578af3ca05604cbd47db4d07750d818b4dcbd2505d6d11cae381f1b23ec2f40da6277b5d0b6b7f4c31c839f0b9fae4e29fdcd958170528b072d20601928376357f3b43851eabfe95a972d6176ed7f96f5e721f23ea7a07ed33dbc77fa93a56eaaac2753e601ec8c951b1562f1c31998301b03a387cec2eca7ff9ba994eb3a57d8a589ce7e237bfffc7681ca1fa3dcfc559c80152bb1c0b7d8ee21748ca807f20c6fbdf72c3a66ea6a830a5928eb049852c2460e5f7502dad794cbb2b673ebe853c482df5aa18c14e9e674451cd881f7e83c219fc8612073d1cd815ba18d7aec4d84a3ff2bcd2522b3c799b336602c4bbbe22cc9d489213a88661e6638d7d3cb1cc7202b66a7ff096ca0384610e757215062f5a8463480090ddf6487c809e131b04d314f631f7b624021e3affea8775d156bd7ed296fa", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000005dcc024becbeff142d393c7e24ebb4140000000000000000000000000000000022e67462ecbabe72cbabd2ff92be537a11521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000cb72a26f4bc2b655f000000000000000000000000000000000000000000000009d27836abb5c49cea00000000000000000000000000000000000000000000000ce96c2d18739d82800000000000000000000000000000000000000000000000000000d5f41f8cd725000000000000000000000000000000000000000000000008883f6e266d0f541b000000000000000000000000000000000000000000000008c334df8c530f7d2e000000000000000000000000000000000000000000000006f020d9ffd1efe48b000000000000000000000000000000000000000000000000000085100738e49500000000000000000000000000000000000000000000000191dde7354124a1a4000000000000000000000000000000000000000000000000f51afb5d050a083700000000000000000000000000000000000000000000000cbf6b9bd0271d19050000000000000000000000000000000000000000000000000001f87fba51408f00000000000000000000000000000000000000000000000507d6c41d8c3244190000000000000000000000000000000000000000000000025dd8654e5a64657900000000000000000000000000000000000000000000000bb69b4488a6661578000000000000000000000000000000000000000000000000000000586bb4925a2313c38a4107f7a1bc1cf105ff5dbe5cce29c167c6d1454b2ec0371ba20730be1215aabf311de0fd5a7e01718e75cac3a5f3d2e9345bee5f0aff320d0cf441b22b940278a9e081c76995173a5668ad1b3c21d52e6515fc1c6111bc8570a97d2513cff4f84ee3292426574cbbd61b9fae99e95fa54d61edd498a00035ccbc19f911aad47df27a1f8d80c091ee4f4722c1831b60a2ed67b54f0d892a3abe9cf9d6000df18f0126f35f6af728bb8c9b5d5b06f5f0facd0ad007c578aed54dd6350d108ccd74a2a74a956663b2f5295ec9f3b56907a436c363da245fe137e4a27ade2d50ba826d38743ebfb8fb237263a20e1cf000ef5a59ebcab9aa9607b8c53dab08008b33f585fe76174121a00c186674a178454a2f1161fe1ce167675ebfb1ff20a7bdb97e3b74ae55d582dcb0becf8e0252d35b442e1ecd9ee727d3c73b1890189f5984477376e807d8fb5294234a8b233a26f9b6c718cf12664dbd17a94f2430404927f8413c9c4d3ba95ebefc4df717868f29e2ea8eed471daa1b632670401475056ba0bd6c349b55e561f7255f437cc48c7a9f7778ecced8c8d58f146f9e0b05ea0d166787c39d8d0bc4db81911371839929baab4dae64cc4d8d574fd30c1d6ba7d0933eaeeaab5dff6cb1af30ad4ba340edabb6dfe01e6f3ade14e206fa12392c605ab00e35de0f3bf35a27774dcd013d5d4038643b21508bb87a59519c1cf7aa122725cf720db8e870728206d50dc85ea0ef6231c890d5c943ef06ebd707e8c9e2a413e944d576a78cc3886020b1bffc604797f597712e1c8d81e3ec6f2542d46bab3e1415b01a176f2cdeb38564ef8a903260938cccedc9f22d3984d92b6585980a340a32614b1ca661901e3801bcfd86a4d1e12191b45fd9d216cb851c7a4a7a32501751a1a4c066f9ed267238c015616330809488b9028ac1041afc06eaaa51442c82a333c6690d7176e596a4f84454367ac1eef73fc39a250e9aaf281aaf24528af51d7061ce7f33dc128198c8d69c5512195e6ff5c7d16e9d25c00bf088a1174233241186c1c93e4544ed8feb5c12ea7eb189f8057720e47524ca15bb6834930409a27b99b6260b9d5df8198ff26dee77f148e06d93bb26b5da1110a5b032d732ac9dd68ac656db90510eb6784c09b5a36d3b653e4ee61f4f4fda273034a8a8a9d894099e482217ed8b5866a4a1ff4beb500a8121f2e0e97c28bb191fc3b85b16dd65970de19f9dc158860f2fdedef692a5801889143ecdd701db172d0437ff30d393a70585b5296b85bbeca15d9a670eab6a0dfb4811882d1983158c4ac2c1bfa3ca560948e794018e4f76b1b2bd25478552837685762e3192112658e9a1e375972488108f6e4ea30062534dc4eba65fd538a3913a9b502f37f411adc3f57187ae6922cf19790a0abe806939d842a4faeedabeabf8ae695fa38b0414ff61f933081d600479b086d53a04eb9332d8640b27783be4808412de388a16becbc94f095ae4f213c20af9322cd1b0038368a1e96bd4adec2338f3fef17f14637b8511223255c5ff15bd5cccc22bdfc3f2d434d6d5037a97869cf0dc02b523019b5dd4f9cd45f07f725dd1d8b0fe2fda8358066a603dd80599f3436763910386264fb6a42c239e760975d435ae996764e3a2c59d7d59c69c87b70c2a295107cea1f16f6c9488b10bc111d9ba5ce00dd862e42d4e06c6f7157fc9f267a337154ae8956a5a22d5ab4ed3d1a57de54132903b2d5499117f60ae26e7a82c1117099713785a7196ff1cccf53bcaddcb8df2b758b68b70bf9aaec914925fd1efde0287acbba337ec0e101459bb740b78cba08a7cb9467a52d94ae55e68b4d1eae304215101d9400a36c7d0caeb436d7eb4b929fee6394c10ed15f589466d2a9f682f6a1978b75fcd7440fa4b75a9ece24dc94877ded7b76f596687d10a4af92f301c3391a9d9ac3bad220771549d0b32dcb0c21f4e9816437fa46339dbc0c667ce03a1456de29a3e69252e7609eebb4743deb6fbc3ac6a7226b5eb7ab4f35f5e8a253e7fccb26dc26f35d4d0897f9f34a98a833956a5d8a34f552cc52b8eedfdbe2c8bed7dcc889be5ab58f16a066ff0738e677b964dee8f77d71ca6fbdf7f6a5f1efec00e95fa2cdf9cb13fe409c243c73bbfda0da9389698bf926bf678cf08bc1a154a4e2740d06e239be0ab6037453f90080c7638fb3a4b47be173fa5fb5dd927386e66d7c301a0b7b84a433b7abbc0c17ee52a4610ac27caac41f9a57fd6572f94f3f0feeb716798e345e74c026fe1eab060a9753bcc6b494adae78b8c48831c4b3068e874e8ae97f098c279efce9fe97431a88ed6383e376b697a8dd7b29a06cd19dbbb19bc26ef7f8e24dcc533e1e6df210f3d9201d0818a2acfed7cba4609dd1f04bba079b62438571dfa4bcd5f9cb3ed792201d81ea8b469de00d554952a10164c6602ba7ee3c93eec521d5b37fe71f00f135dce445d919d1456416e0008f9e1ea2c5f0edf3d8d0e705fd77daed72d4060376d93aff9343976c9f9a9ec1c6fc299607e32dfce088440c1b27910c04f99a8c19bc048504ab3ea1c3149cc189d32066676f85fd4e4b58360c14dce07bcfefb8786990fa3d2d02f55df9d291e358eacc2f8cb961241f3f1aea3f16c859f9a9427cacf73be9fd75855899aa52effdd8445043da2bd17ae2097c5193a37aabf27215edd430732a1968ba5c5cd163fd4787fb223c6425fe79a53a468f901eca21952d954b4f1acfd51fafdf82727e491c3bb70e64d50280395515573b29cda31e01e06f7b2e44caf0b74733a091dc63384e63175842a450d156987bfc733bb50df42f4588a1d8aba142ca728b80e2c5cc3248d531aaac9ab22134aadc1d4b8235879649e448af0d13256e8c70d104cd193eb25bc3b69749015b1977e911bd0360598ead3e73053fcdb62119086244fc8f8198fa0a02425d387d9e842b0f98a41710ab2cf859d59f93acf99b00e0ec230d1ab4cd5571f19f9a9d89f04a9fbe3eb93643915d3083afd68d6ac67e51a886c2e7401e737f324852eb1058647c1ad920bcd4ce15f4a8f1e05536e690c1cdd151b9c5c3bc9eee54d4f92e069b280834727aa57425fd74dd464e11d6e47133a5dd5da94b805eb2de7bb269a9ca87124d0ed304ee1865a1d7788fd4e0a0111fac6499e357b1c303671d1bcb274e49850271998e5c3fe2505a32320745c9f0788d50f5a3f0f7af6a8a11623e0d90e96261a23a0e0be4406fb1df38bba0cf1126c091c192f49185fc1b85af39cc747a605a758a43ed4de6bc969a8e4b94b4e04103b6c7e771115bc25ffb588df0e378b20828b626f60d91609ba557aa16c7a2a86d96745a4af60cef3377e54f1f0a918418915c5b0843a9e8e62defa23fcc7236cbdf03146f9d5cff3c2738cc19c712bfc1e97d04c592abd2a503977876305156635927f604f6d49cab3351b6bdec01b73285797925b9d56231a26b151cda9151bf01dc3bf7843552f07c3c599b32a74b9a1924bae7e2462538fb3d11f872b301e2e979c50766eeb68e2fef80423c0aa0be3237b280ad3b287a0dcc6ec1307118d89e95c499e5375b430f6834447ebc4c975181d5c801730bf232099ebe89d2361ba955cbe174d15ac109b458838c87e0057fee651ab45699245dffe0e0947125172de2d5a309139f81385a6de0665004aaa2a099b3ba491262ff5446cfe2c002136e84f99687d4c2dd3401a0f272c80729f6a49124e5256dce015cdad858b0fe213385b7f84bb2b36ce9195f8698fcb1e5eca0df20b38066799ebac0db9151540a4735f7dbd647255af0ba418bbedf05cf04ce6ffea4263693b6f9929cfaf1ca0558cdfe52634a0dc615b3937be0d6929c9203f15d3b16ae394109cb12b450c26cd6fd4594d042c25bac5212fa45710eb1ae7c12035f2756cc5b79cf1275d21fd427ab4df6fb02fd7e07a7ce5912a7ff309c34b6e8214e214933e375a867e17c84b3a9f7402af2621a1737a0b6b35265bf0e27109ace625439cdda98fe2cf0b8cea574da812b5e8675b450ecc2cb603ba8cd9d23856c0157175a52310c26e16ade24aeb764c5c1c8524595eb4f3add96c7b8dc400a42135dc18da82da97540fd1231c528e70b362440faaedd8d9fc6984f9130a767dba76e3c7a6bf758fa40995ca9d7a5af60270bec0e91cc535b007a685c8c839e9c527c5ab8eb7ee448f0086c298961b8c479e1d2fe0bd61eeb6efdc10ec5db741331f4742b28630fc8f2cd7ad5ed08569bddf91f47e40f0ebc70c2e8d7a5344ae40968a7e06e22fdd9c0a269f5c1d73a857aeb0879fb7fa857a73df6fe8c5699a8fc37cd4d9704391e52af11ffd63ca8cce301c4d2e7046f3e352f3a1a84127940296b9bd7677c48e2a09721bf6df1939a2931b67c40dacbd49c2b0b7b73e173d0257867e03c509a3d11311889591d077ac2e47fa0f0934712deea03834c1f20e1d52b360ae53ef69cc2d098bf4d516ddbbf219dda0bde7756c7a969bdb7258490da74441edca6545b61c59a5c01c7bcd3f276f4511de30c3c9c60a98c5fc07844de934975c350267df1782ecdfed3b15fa815b1eb0844b9a36c33d3f4b6eac1d5e829af190f37cab9509ffe42d384ec7e245b6140a75b7c17af5d44d09b4073dfccac35a60ea12ecdf055f473428571221c87429f479c89e9dbcb99f63dfb86298d9b0357cec7da6532c0bcce9bf9e5a5fe86f44ac9f07f04b76dff78614438b38939c01718301fc3d2b5de0c98b93441550e8d759eb21d914ca217a29d6be440cc2d2368863129b37033998b03ab2db1ff7f43c41ecb606284c4ed2b966ed1b3d7545dd1110808c25237b91337b27c2baf9f82732cca4fd15674245bb6c13c418463c31fa7718d281060f50d783fc5c32a04b819c7e843d15ccb658e7ed3601c3e11460874102652d2746e2fc3a238955923c713d04ae01e1588718ce72710ee2d4744b2b7c7f066e23f97b1d534f10efc221a8adaf64dc52200370e7b67d2a1b56dd5c2fc3cd806315ded78e4cadb5b5b4b51a2ed1a09deccd8f6d7ab212cce6e667d359a56e1d921d85c36ffdcc4aad0db718e2822ba268be4cd25f748cce52d28094aa304d8ca2003e17a0062957689eb6c3c8d22e491100c9049f0f406122b94a8496256ddab51f0e3e1ed32f21fea7688dfc76e43e7a0a0f569fa50e55ad20ebc7281700826926d2b55248000e0a120f542210ce4c3c666232bc6e2862a3c5baff4e739870c010921e85d0d413b13bb5fd7d074c5b85a3e77d5fcb87c2319f6d059759bae5cf2a913ee4800b575b2d10c30b1e960accfbfa92bd0270ea55fa3076b4b583143c20a8ee6d30c78d807de60607cd2469b66b5df59484f0f751cf0bdb2eeadab0ac0bebb994ee223175e760f62893d7c241aa1e5dbb177114b55fbd5f81d009447621e6b9e212ea7f25810a7ea5cc34eabfd6e35a0d3daf29c62fa09ff67c782a0b0563a6cd009e2ec14f1677dfecfbebeb6e18dd9fe2759bec8c2259c64a96c6e62cf64277d23440e9df48b88e25a23507a0b537621cf6d65a7cfed83b9d5e1bbc030bab18c76b60a07de1cb3fc5d9af66747bb0f4a901a930ad67e3ab13ae17d017615cab0f232437159824ce1b76e9065e9457dcfdfb4ce9b364ed06c959cd531913286b79453bb50e0dc4198993b58d03f61d8f44e5b195fd6a3ac0635484d31e96f84fa9217fa2c8cc6745a13cb027fd85c3dc5c12e21291ea1d4143f377a816c048dbf593d4ee59c1115477cc5fe0921b0751d00b36467f73f31d87809faf1f5ff7387d960fa1e9e7bceeac38a4b3951c7887e57c73f639b60c03c9e2f10e27a87b83e774dd7ab1476413dd75c179f4777e339a431559295099e4a521a0490343c381f3704ac29c32f659803addfaafbb556da2f4e2e4e20c9cf1a42d639b15c324488caff0a612c5d6e23afa5c97f6c08140c2b68b5e26529b7abd3c422c033b8ad5cc4aa0849611f1336a1132860abf0496caff22ab0d6d6a8571362cd21cf10df66a695c3a3fae7f5c4cf0b3cd05312075ef875d210ed6618b54cc70852cc8a2ce2934aa228623c7d3e28c8b95a3b639909a68786e5e2cdbe9d83d670602f57f03140395962b50608e083702fd9d54fb26cba708aa32303ec6db94f08929ba7e70015e2dec89d790e53adc8832dad12bea71a76b062d9af4914a0712191517d6c1f295db9ef064765e3bbf16fbe762134852bf0d388ce24bd179f721c4159bbef83fe9a1a2faad7860284fea07825e7744c103fdd244164abc6ebc3d8a1a81dde0301fb25d53907ebb44fea80073371748b3cba4a5146b7e26bb7df99419b6c729bab92e54c5b85b9109c89bb8117d10473fde6e238cf3be539441fd7f265f5c1b07c59a0af8eba52e61614478d50b01fa5ae7b04aaa8593c2b4e3f8f50c2e2864be975e2a0aad0c687128530a4e7f729012edba9b8d5db3ae708aecea069440ee37908c90d60d45203cddecc3b1c760c4f8694a2b31be3b69f6fb5bc42447dccc556c4f4f3dbfb61d9d00203ce2ee1b2d525a12ac890edaedd7eb5156055b878d91521a378052cc88eaf6ff10a20578c029bbfea6e1d7d4b8ea0b70bb3049cdf4083e12f88d74eb75c55488b3fadd884d9fbe62ae22a81fd8dbe4722a27da0e76c31d588779327ce844c20e507f2f516133ec25e6a3c2bfebce4d77f718f53a59118d337ad80ac883b2ff1e0879503dd15e78735fa17e15782e3f05791e53855a12f699b26092b96a0a30eb5157dbf3b5707a16f397dafbcff4e8ee3102d0cbe81199bcfaaf6b3ab751aa32045a578a9ce9c2c312b1847a7871304d4710e5eab9d7ccb6d83f7754cb4e7ac2efb44d45d4165df31c86d03a17a375492a2078373808c9babfe4210762dff443c65be414a814311ac3734b60be87a6d5f50ad1dee8f7d7ee2945cec9db4abe572ad1376925a930daa6798d27f7d35f5f77133dee4a69e0096c7e746499f3f3fd1077c29af316af120acbe1513d97992fb42a9c4464ca87b2d8f9c104a69f28b8415268fc2e828e2824f026f03af66fd1e11f44b76a5c49ae396cc5d3203ca6abad8f756fb8ea36c8f35ea527f382f35eda217e8f122cc3061ab2a2fbe180883059747fa8e1784c0b515eba37bcd6d4a0b51ff843b8d53948501be8a3ac98c02bd9fc903320b49d12e725916c5283f9401b27aca219a88cd6af41323a73b10eabad4274edb5e649ef93d3feb4cbcc5efb28166b07b8e7dcff384040f8c59e62c37e59a90dc87ff0f7610418db2e75e7d7a82b84a028ff05076eec82ea0f4e7fad6aeee05c0638923ee6352c22bb426db70b075b23591b07cf49fe34eb90b8a4b806d9cc9af3fcf24ff60a2d4ce065960bc12d55ca8b11e60998621555ff79251140cc84f39f339c102f774696e94349e63e07bb5d0e9d8a10f5f983deddbbf018cef93fcac66850d1bb2d378a859993e43d135dc83a4f66ffea12b85fa5b8df46beecc582a2e406b09252da9fbd01d6208501523ae368c7179d70c238ed89f951dd6d7a10253da90e68b225eb35fac658de0b7b96f652ff291cbe7d64daa91d40c9dcd19ec109d592624caa4358483c174027b78b2f933df2f6b098d71d2ac1d6aebc2fb29361448d44ebd93e3468bf22110e8aa311aa8db85e773c3067585caa497802475fbf34b1f148388b265276abb5207492d086f609659abb12d4d48f3a2c84d9c80b069025e36e593093fb5ebeaa1725c6a5df89f634e9ce26ff556cf6f88d7b67c7e193055ccacc20c9eb78ab721af3ea516e38ae26ffe5a7b5c1954f4590a4d52578cf9153a9acf33af550c04926911c6dd114937ab8ea8147d08c7db5716275245fb951c767f82430ccec444501d3c16986d95134976e7a7203583bcde83af9a99aa003fb4b55fa03f94c09e82329669086fa0b5fc56daa0ad6df98574c612c0788fba15f4e3fa1d3c00ea3ad1a32b7c183d6a097f0413f606f106a6cdc0e93a7ee18830496f8e1731626428f01bbfae67ee1909bc7d84ffe61070be9ccfc109b5e7fbbbd27452a431187c53308ca03e55d4845dbbb575edf975784b8242889b19cf9001075a3a0d084bc2daf1be59a8c0addb7c6fcf3d922c7d19ff3981f5c9645b031839de80b56da2982f426ce8869036679da3970739f24bdfdea24526fdabaedc36040f2902b1b1119e72c14c74f69aeadd07892831a5da635c6a312cc1139f8ff1531db999dc979200100719683749f6cf50a21868958e37ff4786e7b5ee1703d43e9261f52d12d59582986a895846ad4d5c7ef17aeedc053e72783f575d909aa2f782938ce8246a11e1372c04b95f0c9a1fd6ee50f1121f93cb391c50d0c958a37a68add49cd5e556b25a982c3a0b02603edc7a09e993aa34d7d780e71e955a6c56d5b8a16209a386e2e989ee588189f093e06ef28f0cb18aac37a1b3c369a80c40b64fdcde957ce4f2ac250b30125a6bb044d6cbeec900598bbab4ca89612b3d2c264a28f034873672e18d7f11e234506bf1f2309b7444f8db2c7d5f5fe70c612fb85ea9dbdff796807ca6ec9c93676363ab70041ea6a529224ce93f1a0abc53a17701c0c3d2f4f6d2ede4b9fb18914a4113f65e3c0c6111267c4f6be1fb8046ef25bf35c44d19b842ae2fe2b889e477c54c98d82da4c7b0612603d9f6e561a05948ab0c30e494e0b2635f26b324ee6ac274f83f257657109d07720342b7b7b5fa7637f477d41af6c08a23725165ed2a19148c9ea0ba8ab684b496108cf65e5f4e7f0ddf795c404120b518f3e1511ea4f7546ebb2b1caab95ca3f24ea068450b7a0099859ae7c1ac908b13c49f09e88fc97a883216872bd6858ebc1fdfe5f52dbd0356e05257d80320fa187705c51f66366f2f05466b1ca3c892961f9a589a6cb44bb273dd4fb84551bff7f3406bfa4f63c668260021425f9b7163670447b8235423b3710b5e484bf3039a7737aceb27e9be16933623f1cc9c9082c83599759847de30c04678606c6003d492575ac04ffba536c55e0b25a03951405779fcb7d3cfd98fbf3df1e61742d76e67149ddca631eb12e528d988b718e63cc9619433b95482a23ef3db9d01a2005fe40cb5a62f8be9698ce69cd4b9db4778bf5febbdde68eb90f19868969d802ae7a7d22b8d6032085d6040f6c03b6d7492886d6301c9afabe287c6c45b0292b310e8d9d7449443006fa8131a2f3f5656802d209ae64072361a13cd7b0f2281fab02ef911d09729456a937561f28b22445560977a07b0e00888b90f5f476c92db9d5073658a3be4ce0ac5d1db0fe45be0b1c3bd53829f62ed81d5ff6d51ad31621458d9822ae610b033350f9c993233d79ae279ea4264e67f31efb46dc276410248f27d94a51f4c7a665bb24cc03872c8cfad6c883481b414686d970691abe0fe37c44c605610848a3076689513895acaa05834a3b9e0e024b345cd8e1b87003d5d7475d881900fc5586054fa9234cfb46bea65c71eca250a63844c749279f2b5b49120d771d71fc3500f1d125662bc80e13db85e6695c40b5e7fd94ba3f2122663e719d61c8829a3f55141ca58c5ed1e28beeb46bae86567d9b7808fcc839235222a95ecbe861aab8e7d0c4e600144af761a5a540fbd9def7aa41cfee261e021e3286ab9b59f7a311d01c19931e346a5bb1e1c4d7c9bb3faec5f1ce5d2643047b3bfc0e3659c2b8b8f7fc07dd843d506d60a8e65056c2aeb3cfbcc25149661e2c5eb1eda809e387a860c1e9a7f37e1ea8a18b48cfb239aceee1641fd9ecc023c4925fa502947194eabc25b3a8948861d26414e21f19e7d57d23ba4d935a281313be2a19396e46e733a2d08840076c2f9d38c7891201640355969f72e9492e01f41e5ce954612273f29ffe616557a9c01bc2ed58536ed6e0171a7a9631df70079f38ef26f2bd6a9d0fe7e253fdd1fe098e9e59c0fdf14400cb83091242267f205c26520404b3df973224a3564faef86238ab4e08c67bb4cf64f57e17b0ea4b1cff7bfc129663c0c261d026fd7aeeb7fb358b296c7202c8b2eaa839b4f9175002835b9ab3ec3ce576da4ad7ad83dea97c3d9ed7bd366878cfe8a5e5aa28c0df0139e89362425d85b45523b95a262b5a68718df8f13c6fbd052ef97bdaea84ac158db8e784574a71e7211a48e7ef64c569161cd19539ea3d1597e05b0a83356d0e4055519520120d0ffe93ec75da6b630acfed959e69e25d46501659373bceed1306c3e92851ec6cb8dc53f1aa72aa8bdb0d85a815d3968a9b034ccb292a66b310935d44098cf4b92a29ac0617d8dc6322e4ffd1afeb5813197719186dc6e0d600dad3781e520792127fb8fd72c9702411c0a1f947aa32f86510936893607fbf0de068f52c56a8691c5d4c02741954d82563a90dbdbde6a4e4c81fb7d620ca76265fd84201581590686edc35cd730bdecd186812b216770d2b3b15dc8a398be425649dc6e4f3635a2b9f8552be41509240dba79b4f056366063fa0f35bafe42b110ca07b7f2c8034386bb0dc771415aa8cd885bc06160e77bf85f5c3655d641624fcb342117f94e4a5afcee5b98a648baac82063d897ce4a82a3feca3bd6ef6b2b32f06f9f38eaa15fbd3f49e6f411ded6558ae35c4911ab1c316db2b01ddcdd206ef277976083b82e474813b23b4b8d9780e0bb6db3860a269b5215d33878c207c796c7a764bd2bf1bf255249cead62b9d7b90051ac8375a1701a271a5a504b025d4982dc7781c4a68b774c5e14d7c7a6982229b6a5db2a69012f9dba9a444e178223d54934f655c362925278725824ab9bfc428d2026d7725f63f6b210d08103b372038eb05ad0deb2cddf7b9ca8871fdfdaeecaa6e78480148cd53d772e002fbf0df391012015bf65477a51e31a284c0dd71fa342e2922818119bc930a61500f7c8b0f04920ba24b379b6cc2952fb3119a8b363893def2a9ac3fa5bb5a36e19f5e706f9197907da882af06c4935b07323af3293c75d8ddcace5e0e5a5aebb0717fc5308f336c38ecd96c89c9f7422b335b0a4e18f88656f255e9459d477e515a56275db5ba929662aed1b09c814b5a3ed4920039bd65300cf52c70fb7a42a1c81454eac1c3d276f1c272f81a95250fadba17c9aca2c0583aa2bf5b0d2873418a9688b46dc7ab1f36aeb09ea1b00873a8ff04e4bc608d9e35b53e7735fd5a01dbd1eb40cc29b13bb21e85f00fca65fd0983459d458f25a3bdbb09b4094ba692896c82fea2e9353d9bda7feedf2416f1507b3342c0d3d2a2a50809022c001bd2e6412debc02a94d461ece20fdd765c74e78f6e9e43fd14d935b7884b716a0692004490d547b1c97f3b60288eaa7daec61698fc7e0d302ef97ad1cf8cee533b816f6f722c3056c097c802609d9c7b074c50e75ff09c190d2cf732c974af4097623dcb10ab54bfbe197b4e971c40d664e273b6dd3e93a40a8ae5187ef842159940dad07983ae2f8e04736680fa307ab50921708d716bca4297a7ff0c816b6446601987090bf0b69c6415de518d73a56474ebd658f8d1e9aa500e0020a6c3151201b11a3cce79a98ad901722371fb8fe162f465e6319fcf30209128c969701bcb70001e8aa66cd3c38449bb2e520f07b9e1fc2fe710ffbd456d39711efa0832ace2aa3c12f7793c3b97e69d5e28f84d9dde4cfc2990b64e13022e5472b64c29c782e4cbe5186a9cd9f5c372f2a414838dcebfa655361de8f64b3155ecca983aad81b51c65aefafea18e7d0a0430bd86f301462df63650497152a83d5518eb590ad03c3b822cbde2466c3571d685b8141f972d64a770a52f2912ccfa1af908e84570a58db6f335e36a93b8b46c8fee6a65d1dd1f84aa7d0b022392329cd47f1acd8149af8bbb40ba250d374000d409fb81cc9aaf8c8124303485900e653f153c5d108e2fbe580e582df090d041a64c9631d114bd77943a103f09961f818567c3c711c3a5b6daf0e32e910da373b362b9641706892cb96419e5a8c76f29f34c05c4013123379388bdb0e5883b299d8469542bdb58f62773c942c5707f5c903f88c5e056779d741068745343dcb01bbede6be417932bb0cc0fd1c86270af2cb63496e175cd36612fb28f4a8affc5dfec8a844daf2e67e83ad7db40aab6dcfc23413a702336cbaf76a18fef3c7bfe65a85cf8e99d59537b8afdab62cd6882d17bd1cab00c6fec1e87fb717719c8f1f73751ce868eb93e57f813648d813dd68f03f30521c2976a50f6721e68754e5fc7369621b74dc6c2b3a407065b35b5dabef052d1a01ad1cd893425a4d56671a57219b26d215fc4bdd5b53680c43453081304b374507a566e2c9d5827d3fc804809d7a8aefaafec10fccd87763c298c59dfcc7a69318dfd77590a160094d3a359e73ca0c1279cd5fd655a128f580b7fc2758ec480317deb9f9addff0b92367143d9cd8f30c84b15b25eb5f4a47ec56caaef58a6ff1139792c89b163437743a4d60fbd172e18b270d653927725b28ec3f69a8eaece000f3d850d877a132fd182989ac44e2aa29459f7b3914f4f6030076ea47ae401b203e48b45fa72c752b9947086a35a67b68e8bc183f3e9e37397cfd45312ad31908ede3e9e4d8c78e9e224e1a42c56f5bd1980e06e9b1d557ba4d57493cebaf89100ac163e836ee4f1816600283dba01f6b1f1699f0c1ae32630a712b828fc8fc16442afe40c2f51e6e9ab05fc7190e8bb2415ed1e5a42ce7e4c858005c353d910efc9eb2cd154c9d5b03da687d58cad45ddcd6ccbb36062282ee94870bfee5031854ba4f8c71350dbb06dfb2fe67ea4bc8853f2951f36f89ca530c4ecbb2a3b30869e7f23b5b62505613dbefde6e5a8670649048987fd3f588e459fcb44f65c91324c04be47f7bf00f94bdbd730a95c86122be5086d212e10042e980a2571cb7164875082c4e04eabce1d7df6b6b74149cf0d6f5214893750882693d6837e6cb2c846f42cf78fc0e634cd304dd118bff8386c3616be40b1453caee5b2e833a4817911ac39961f385178c49973efbb97a5ac5fdb03cb61084c3e58ca4284bd78510e102da33ffc70684ad4f19bf2bf727b1e19f421915eb4574442db4a356cdb31b5a6ca64ba0b4bf26344a21d31b62575d12809c18f9afce06a7e61ee6adf8e205a6d7e18315546fde9232a5ba7f09a05f98a64e90480e069a3dc1e788465d0211443339feb7ae07069444094267bbaeb0886a7395a2c858b74bc77e9c12082916cf2bac5255804cb043cae050335971a55b97ded520eba50eed6389524dc9cf1cf873a57c6bb3c9eb90393a184f19700736c2a1bbee9d61bcc848e5c743387b058d210bff2531aecae2e31175afea3ecd122a5f7a43ab8c7ef95fc562d5133604e5a81e8dd44d7dde63dca73a34cdce179691cd99dba061ba1e52664d6e8b531317f9cc29b4e6737ff28a14c91ae16a9509df249d4caa5d7a9accf203ce43ab0d4a819495cabbaaadbb284b01061f61add5ec9c0a9d39f792581cc1201ff41918dc8f1749902f8d4eaef9bb1385f4d84cd68f9e86b0adb4c1b961f3a4fae3342fe7b21f2b28fa20671100694f661fef4ca029f391f9ca4caf3cc19e39a4981f131d4f538c71f968fc0d9f03cf323fb1cd48dd9bb900876c300a022f3e817a62245004d95c4a1b15d78fc5024996506557dd9ac3bcdaab64e61559e8ccfaf4480c2bc8a49d41504a25a33ec9f6e81b37e9550f8df92d1d49f3df4724c28ba71c1ae21a17e393aedc796558a02c5d671d715aa45e42efba7af0bd351bd712f9cb21000f926d7c0f172c62aba013fb20716bc976182953d9378a505980b02d55470a9faaf9bcc8290bd4b1d1f3bfc1e7a3db16f44bb060e693037eb8c2630853782811e636bb7b14aec5a9dd3e96b4f781cb0827aab48e5b6c3e3c8fb903a57f291e35a11477edd96f428ef8334e311a837592ef308cdf95e6b25bcd99eb82b7470af7f98f8f070045cc5baad5dac76c953ac04bd81c2f3556964e3779a6d49bd125b669ca6ff09c379f03bb30cc3850e652ccc14af965f1e6f7304ec6c17f0b8f032f2223cf5ab06bf1fbb19b635064e577862391c64ba9c3ac9869bf77b04ecb2f304149ad0dfcf55e5150dd5e739c170e146e0b68e778880c556fab86fdf98c0b4a8ba767b05fc1b31ddc271538d4d74269697a278d281e032a953cfb62016213166fb3698dc2527a7c37dd6350366036a9bbe0aa7e36349e83e56e21438dfb204239e67fbeac731d256fe906462573ef805387cd62d66593cb274dc1329edd1be34024d2c8c7a529346672c40c60cf1014b3b41389d8401788bbd8cb2105472d79f63e18e169babf3faf554f155eef0895ff746fb7bc96e4e06b0c283d291f024e9d2a47dd1fc370e2262a15b81c5c5a8744919f50bafdb4e8c51df6a934a120eda77dce60c20ecf47300852ba0eae62c5bdd58c292dae1dbe15bb8b7336160806a01ee0096f1183ddfa1847f17e3efa20786a469ffe58a6084946b2881efe1041ffe6e5a395efb68fa39ce73758041c29dd0f2a8c50501c97e761164a1de90626d0a72ca8083f1f970d8944cd3f269c919f52f510287b5faa74350d382f252031db8526e96fdfd680e5a32fb0737a7dc5326b6a1f3c69583113719b4d8a5021ad6adc313fe5fdfd4b0a3d38d789ae51ce92b83939f8386978617f43ad22380a1c75f0fafb5b46d62c89adb165184ce88b27f9e572b9c763fbc2cebd5124e702b2047e10a8735aceb69559dcedaf4a248d938728d651a01bf76c3e8ff7572f", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000002996b0c00e4dc6ae412f0b46d68dffcf000000000000000000000000000000007758cb4d71d9cf4b6349037b19fd145101cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.111521499, "runs": 3, "total_seconds": 0.334564499 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.610321888, "runs": 3, "total_seconds": 1.830965666 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.559050209, "runs": 1, "total_seconds": 0.559050209 }, - { "name": "GenEsiSss", "avg_seconds": 0.124032833, "runs": 3, "total_seconds": 0.372098501 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.226692527, "runs": 3, "total_seconds": 0.680077583 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.500284375, "runs": 1, "total_seconds": 8.500284375 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 49.366586083, "runs": 1, "total_seconds": 49.366586083 }, - { "name": "ZkDkgAggregation", "avg_seconds": 21.116986167, "runs": 1, "total_seconds": 21.116986167 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 1.465883083, "runs": 6, "total_seconds": 8.795298501 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 62.328322972, "runs": 3, "total_seconds": 186.984968916 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.200691667, "runs": 1, "total_seconds": 2.200691667 }, - { "name": "ZkPkBfv", "avg_seconds": 0.336088264, "runs": 3, "total_seconds": 1.008264792 }, - { "name": "ZkPkGeneration", "avg_seconds": 1.351367042, "runs": 3, "total_seconds": 4.054101126 }, - { "name": "ZkShareComputation", "avg_seconds": 2.682164854, "runs": 6, "total_seconds": 16.092989126 }, - { "name": "ZkShareEncryption", "avg_seconds": 2.506225536, "runs": 24, "total_seconds": 60.149412873 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 6.176445291, "runs": 3, "total_seconds": 18.529335875 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.100550749, "runs": 3, "total_seconds": 0.301652249 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.221828033, "runs": 5, "total_seconds": 1.109140168 } - ], - "operation_timings_total_seconds": 381.986468376, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.0715085 }, - { "label": "Committee Setup Completed", "seconds": 20.22829075 }, - { "label": "Committee Finalization Complete", "seconds": 0.006707541 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 304.143739958 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 306.698077958 }, - { "label": "Application CT Gen", "seconds": 0.313315125 }, - { "label": "Running FHE Application", "seconds": 0.003688125 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 79.884513625 }, - { "label": "Entire Test", "seconds": 410.2113715 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000001104c69c76ea8f5f6000000000000000000000000000000000000000000000005c85075d6baa615c900000000000000000000000000000000000000000000000eb6de0b1c015f0f2a0000000000000000000000000000000000000000000000000002a0b5c2857c800000000000000000000000000000000000000000000000036176084b0205127a000000000000000000000000000000000000000000000004549377f025fd0f40000000000000000000000000000000000000000000000003ff07c8a94fbdbbd8000000000000000000000000000000000000000000000000000185ecac0421330000000000000000000000000000000000000000000000044f1f17a53eba29b1000000000000000000000000000000000000000000000004096147c1d40da636000000000000000000000000000000000000000000000007c2eaade9aae157c100000000000000000000000000000000000000000000000000023ccf2dca92b000000000000000000000000000000000000000000000000e02d12f90edb8cac6000000000000000000000000000000000000000000000007c223713eba54496600000000000000000000000000000000000000000000000ec928a9ef7dcf2da9000000000000000000000000000000000000000000000000000282e9bb83bee61a05d33c39ba3812376a494cad4c46e07e18763a4c0cc37c7ac2f3635530c6110f00693d937276e524dafd5fffac373d40ca9ac38bc4003aa97fa25c28eaae471e6801e127832204ea5a325f05bb8550bd90338e17663499f9908e07c1a27a4d25db068d58486c5ed1d571b154613b2a444f9ef333d6490c341531f43898470028e20e0d46d87d6e6a8c672bd88dbdbae7c0e0a9d06da0323fdaf7ee61ceb7611d45afd3c8ec55431dab1b693939c2a7f313b832a28959c3ae3d30629655cc051ff1a5efe96161c60a46f240cb6ca1a531353aec9b48b52ad525bf6481f17e0923676a74fc00629f08348ca4b4467e32b7907ccf75e78538096134b580c00ad016d8f24d8b2251a384465ecf7da74301f64008beb9c8b71af609a0085afa8bb10891080d365806d9ed9f21901108254290c057a4b986687bebd3eae86d9d84d52ba59894f06a73d164ff99fcb0d13873279ce4f307eef63d2c0ba38c4624e95e009c113dba40ba429e2620e10806283dd5fad8df1b6a85051c1f7167194c70620f370484b9bd06cf6e6199ccd69d2c0e0db004474b4ffc62084f7697d21fc04f1518380c30df7edd6752046611f209863dd69ccbd4af39b35e05dbaa35d7aea81dac6609d0b9b649962dd23bd220709758af7743abf5395724f809100dfd19cb13ee772bc80efdcb985314bbe58feda8ac8cb79523fe9ff183ebf3e6835eff81084ebea45358443421fb0f2d31a11171c0537eaf216aa68a5bfefa0d02b1c5b92a10ac9238f294ee18007a6f18f72214951c398ce84c3616bcf3c79e0f2e2f022a0c90527061c47a42c43cace4fc879a5a326863a6f82d9c6bb420290aa900d8244a98b1668bcfcb6023d101fde9f2fa579bbd8947480f7058466d6000b38c5c1a4fd3aa5794aad136c957e92be80cca60120b6d839e54f1036f9e857e14746f2433e79c2a2df9f7ea53e60405fe812eb086dfac1f0b0c638610dbdd208a28f2260c558292be011ce1ad7ea6fc1a18428a3199764be0e5e4169e261b655ffddd06ca1104a26cac71683ee832dcd3cac2ca05e81de30bed5f8b67ce620c6f88f62d90a64a9659cd90458edbd62bb6cc9f8d942669ccb243386ee53c938746a2462ab0830a5ee749ccf6259eb0ddd308297e3093c1e2bf8454dd5e900345907a8608b9832d8e40d08cb3dbf14939398e8c032b1baf82f3b45da26fc2aa8a69639d1379060a52801809840916499ab8679e0af7fa2b5e549f58ddfc8c60090ad5d42fcf59ab9661b29f6aee13a20e279602925e6fffb2328ef1334585e819ea8002085571a91540444e60b8c9350331f5cdfe8ba889466f4b26343d3b179b436d1015fdbeb6b1586fec95a850abe17e3d23dbb519b068c9c59ed740833e71319db516be4631e64a10edbbdfac8daafe9004a1dfb5631249e558132163eb6429c715143679b05e2edc9c236a9a7ca760761c3a117bb03755bf8d04aefc7742ab0ba811710087242ad211047ff4f6b9c4f8faa4640f53721a400815c301a8b079d1ca2b7184bc65e7bc3cd2c380df2376ec233411c4d429fb12795cdaa8b932bff94628703868796573c773ba5a60dbe2dcfdba86fbf73e2cbabe306511dd6f71509607ebc04687500585f37d8135ebac5386803439c01b36e449d3b029c1576fc2f422160f4fa03d8f11c89e134103a7918939563aad5285049c66b3479d416852b11896f8b31c6d91580c0cc03bd04ef40a9b201d686cbfec6170ee5a1aace7c7f2248c548038c030a7c267fe72915cd397e273c42561e11410672a1e41f71827eb247b679c4eebf11bf064f256f418ef2364b7eaa29fa9cfbf018eef1952daebd915d423652908cbd25b28b3b07d28cc918af5ac73226b02d6340d02ee74f643db15c870a4de33c870580fb55142ed86a7b1efb02d4f5d1d6e074137126f61818d0dce8e49b8aff13f2e80fa7d585aff6147ee7265bc92df749635d9cd77bfb87508e4dd08b4f437bb59702e34ec8c390bf6e7455a4619bb3586688b16e1045a9c0c8d0b760cf3d11357e9840fd3e702cce25045c7835bb1b0ce6275af5571d18d1e90731123ccd8bb5b7550b8ba901f3e94958acb3e9583067cdaf68cd913726817755888db2a7a28e1a4748b9ede248fe0ef76aa51bf8e5eaca3049ac9dd7984241212bdbe5b3d0d4b586a7f468056893fbdde4c94203e8ff8238fa1a028ab2b059669f73b36030f5205fcc5958017fd26f9fa8d827b6cf11aee31fa922e8f0c21c5cccf059ac7d29e54a4df6f1e43b8a4b22689859e8569dc98990b9fd1d1b2222e19e76cbb31fff18fcf40798cf42482bc063795fa19dfe65eb6e9a570b4c41e85f9c94ff56701adabc0cdb76def2f5770270aa236e1be1c18ed64e51cdc911a1133e096a0272e0675c680fe24f2cb4f9a201401977945b920b02e60c84f3309e8ba8688c6e982bc9357327d438c5d34986a3ee5c88fc5886a4c66f1d14ef625d0aa01c82cf320cb1543887eea14b4481733f213beaef47ad92251eb2910bb1c23b4c930200102e6ec848b5470cc88a0204f6d4a9aaa1cb29e808e3cf2cc7622ea3e6a331b26e1d2a78f90b99066780df63afffc89bb080ef954828fc64a9b0e589c3d02f1f8f8c03919178dcd7138e1381f54749a684c6c7f852b0e85251427b6dad8334cd11f9f2e16ab2d7654d1dad887e5dce9d872887c6dc9d07f45481835c2ae47a1c0c9b680a009ce8218506f77c3b8b62654b8fb93043f60dca8992c4ca79d52dc8431a240cc5d01a027438fa19c91f638b7cddd6db1a1f6faedbe1c7eda2cda8637e7e0a2f49d2ad0109f7a5bbbadf6c8478ba78e716fab26944e182e437f00afe8873f1030439bfed6f6bb3abc39bd1882eb2b6eda0078c680e924d942cd5b55449783c3a8f192e24aaa6c305167959fe1cf58e9065a7784325c2d0fe06c3e064f99ebc1570a82cd43c33ea443971e0f434ac11883fe84461b9821746609c618a3362bf051334253a22ae38e9b6773a528204c0797297835f3f61549c8cd8ad16bb415ada181c52d843d558dea5cd548c3823ac4e3d6df1838e0073102582f62d7dc0ac66710e9d0e8826ef77755403b7f12a2863faac6dfe4cb1914e29db1b956406e9f46ba2653617ac8ede05dfb69035dca3ee5c2d00d3a612ec25b4fe71e06659ef23bb53a839b6ee48c4e38207109e68d3e073fef16eb062dcc3a22a15afb381581cd7e9db2c78f2b801ef7104a5ffb41dce92caeebf6141190682aae5ce74e6b6adc7c901eb6db76c548746b9170a1c81930149032b19c0c0ab947223f7d82d1f7fe5930eb75cb6dd0bf5394a906376146da02731747ba17ee40b5061140ddbffbf7b01d0e6661cb6d3cca9eeca0a0c4640c5d61c72d9819f1b54c8878b1cacc853f1ca2e3ef455bd082bf5bf60e4b13ba7569e7804d5b1442193595af32b92e91b90c7f7c9999f7c665a9c7a7f5911d73d415c3339c6f0a968a68b6475c1dabe40dd46087630e1fabf2155f2017e4fb42180a3319e7d7063c027b22b6b8389f83c9aba5a25fcb0cee256c1c16760db9bdec0c82d90800249d026ad7e4fa1f8635c4ee66ac2ecc8ac01ec76d44981128e4000cd5a529b4011b6145ece1a1d5404a453d8f28fb6ecedf4584fc8a952cb6f9d6df93cafcd21eac2a8b63973d1643a07daf8ac82dd5b054c0d25445e09978693ac63ee2de6516fc2b9c0d2c59aa561b6029edfe22e85ec79edb34bdc7617ba11cf3d4c0f02b13c2eeb3c0faa2fd0592f8303ed4bac9e9f81e7c627493f4a334c539b8acd96f02bc66d56f149f0139979ef02b7f584ae703925e664182e410729af2b9675cb406169d27e032bfcc6e540bd4b7b1b11054f83ac31d5c5013e0ac3f6d6e6a97ec1b08b22c0d8fcec02e23c3d2f56227fdf155a9a7c363854deaecc5321bc94fc52e50df9ac72c04b4fb74cbf7cd86c427f9049ea0123dbabed674a4e2de9652290ddbf03a1835d5a92ae4ef3237137432ccc8146c33fbd50155eb4e68cabd0208196843c201f2ed0ff53d389b7845fe62eb1e873c6f5c9c1216ad8ffdfeadfb1718e74c50fe500225ee24b08374d2a8b7fc17d324f50220961759194b39f844b12739e80621a5960316675859bed49ede417bf5a5acc2e4b364b70bb92683986c1df944a9a3b8cc87e3951e156af6abca648b28be2b74e6df59df0801edc236b129ba40af8c1c9f93b26fd9a45d102fa7bf90e7f51f14dd59d241c4327642134210376431209d2593785a6a4a6f042505387ab87daf614bbafce0a71426a7e26d2ebe8f7a36e23272d68e4396afe0eb899fe01103e5190ad987834ca9971bed9c1030fa67d27207d4a97e2583383f5904f0a0ef868a6b8a0a2c85fe1c9554e620130e6c08293a578b5a28ea7c0021ceb00c5e4e171a08f2097216d3dced274a6407868f61297f9fe9e95cfa52375e1390c2a034d849bbce454638828ee58aec0b0e171dadb3c2ba34e649b6df427a1a22ec81e2406de5d9d3db670db9198fc1070ef752419ede5f9973c2105bfba9ccb21d5e02ea1e13024a8072d8d2816018970bb8e226425c4c82771c7400941b55dac1a82c6ee13100d8bda00bab5a6d4e0111f101af580b036e177b7e88d70ebf1f57d3d9869f488de7ef2e41923a71cda5153c0f99aa741088d700453e078bda0ac7cf72c4d40e5755f51e9f972d5193161ffbef1f56c0eaf0507bfe67e000aa14c77e2fc597669522fd7553bc3a340e622190378956119d6213836b644a25648d9b3463ff256fa07f431fb1254f38a2540d7d8d1ba4d4000d5470105399683fa97f32adc17549946b7d4e33a06085ea451bb9b0d600fe3202fa84f96bbe9895918438d9d23440f5d086cb4624eefc7a2c09554cefe010fc8412f7dea624ecba0b778302ee742b84e396dc43d3c76eb2b10567ee21da53b51452ef279344c3f4cf1797576436ef727f496641913bc0f28b2dd03f71184034d502788de0d7d8caa15ab98c454d2676e297017823c35c061419bd9a63ba9deda91b4a7f5cc27883af1ce981a40998cb00af959ef782a4971b2bdfd91e38d7045ff5b399c5c097e014ee9959edff45d11affda024527b1c03417b6609dd14487de0f2ab328f2c7066e2fe3d18fdad528fa097746a24139bfcd1361368052ae8ebd22969612d971a162c5cd161da46aefa32d4b540937b80ee404eae6eb13a39ce56f8fff92b54da51403fdfcf7946dd8a43ce26cdccee842af2f6eb15827f305af101464d910fb943488e0e5ba33853c6206eddceb9e2575bf15ab59f5b519e3023a368f7c1584b1f9e4e185f05cdf6b4ae976f075d4db94882711c2ab8e50b028f8f181b95604247e18cefb32c187176e0c4264b9d9e78f7c053277dd97cae1eb03dcf68ccb9c14d6f77256486d60a6ef3c8dfc54254ae5d317284ed44a8c2ef6ddf4aec97a529d841a43fdf3bff0d07776383574af343a6b2504328a61ae7607b1e792ad94fd63e0f5f7a714febd8866ecf4e93be767a16f2c6a4302ed3c88f3e59bf92c5343617ad9051655cd702ff03e0a52af7f0759fa18826dd0d6c0d6b9b58a943c485c911cc2e83d683725ff800e4f44dea9e5747c064f56dec7c38e976a8667949d972a1e8d5cf265e4f07d86e2bd4c2706fea5e50780f6bbcf004fad574131b2a5d060402b25a79ba39adebc44adec286e2641d02c06eb8264faccb1c5ba4cfe130df3ea472e3ad9df249f14eb4db5f2a7b1bc271d67455ab068c58b17a0eb2445f60d9788b899e3779594819895fadc61cc2e080daff29cee7c04c152928572b235c790f229121bf087c155efff507d086f273f11769248554252c8d5972cc79eb93724ca839565986fd0d23149a7d428b2eb18206374aaf52de2629a8215536912ce04303d8308cbe65b8b76a963b097519a531615325c2fd7a1cdd84daeaea5f0951ba4421342db3b0225bd35b659e31cff10180bd6dc65a34a851c7c9d943c9a600c76aa179626a52d8e9f77fb03b5f03100037dc56ffa38081b2a6db99e9299075f3b2c4cf6039d9586cbf123bb759bdb130475385704108ec22012b4742792a05037da6aaa56e84da60c467ea7d3a908e81d7ecf0f95150be7f4d5dec9a1dd9dc832495542d1c376395c5e7aa334e020a21b28b9245119102fd119e81c340311a9ae3749ba3384ba0f1204f6293e3937dc0eeb2fd7a2ac724da6224dfd2dbec16d3a3af4c70ef9ffc653a52eab8d1693a61bf5633124ef3a8eec373e13373abc59d5993e616593eba7dbb5e248ff802881102c6eb060d5d2b8968e9eef372763ca5183b873e5a581e9e865f47b6028bff71c19cd3c08fd68a23f0d9c99aab607387b9c6ec678f9455616311768fa832a5204da68b4a1d18935ff4523e6cf72b1850132058c7f150c2e126196b6723f19ad2fb8db2bfb259f034348221b3d3f87b85780619a6d54e67cf0a55965862b7fe52e23144adc595bc2be1a48f1c1cc41b41dd9da0dbe5adef141730e340d04585616328ade11c06b5003e195f1e3dab9003fa8013ffd3f7e6a67c1f51ebf65dad418a861e2f11f2ffe125ce67efb0eb74f33bd131b5a5d11b6446a8b3fa207ed8b2285a3475847e33e625ed4e7d6533778b595aff6607586bb0412d8210fe340552e3583fceba101476b4fd039726118ac87f80df3cba443836dc2a53fd6fcd34e0e18d5690c79a9e9db8026bb60f070b81acd43c1e5f7030ea7dd2d61bff42ea430279ed1125758d018a344fb17c2592a3d2c34594303a65937825b43c263ceff01c029ef10cc78125dd9f4a55a19c460e1ad391a367f976c8d14a2fb91320efe28825a32c3b76a082a9e42428bac2e25c4892681860db150836148b3b1fdb13d1f64fd78bb318ce7387682b34c821bedafa127f2556a9c2687178ebe195756f609fe81400fb471b4fb1d59f727b378ed82d87dcd5d496097eb293d973564d398058ff3208fb5c9cd219328dff54018be2374f4a66fa489fcdff636810ec5288a0f1428e907d19a12444d9075431e049e594078e1d039e1260075278fb374a93e0105ee257808016fb5c76dee7c048012653bed21b536bffc786e8a1d77a2b8290b41964caad2025868bca96f50308b0e8d7a814ec83b900c8f77583e86bae18a18d2d335215664076fbe4a32999c11aad848aaa1df092c39ba98380a3439943e1ee02ed6a5098b95951eb7e0447b7df1eef98bc02b65dfe974bb92a4ffccc41c2983166df1755e1dfbd5545f0d62e8a27daaead2c734a6e61a196f7c253b967c1528ef00362014c7cae847514cfffb599397f6fe6c6ee72025414d2908b47de40a84cb4e3c3424ba0329707761ff4b3262835631ea1a3e164fedf5077ad2ad20039921b628ef95d8480eb14371130e00af763739537e2d02d6f125589f25eb850ec1140ed345fc070944b3846169aaf337e145e157dba7bb5cc8ae1919e050e01864dcdeb4f64782d440a6e643bd1f58b00fa425237d8a1f8b9a014d7198127601a587ed01e73a590f2bb886b1526b9aa2f577b51ab869e9e11fb795efaaa6c31762c2ab563613d28bfb2c3d59f769eb526bff8eb35ce7a97fb655447093663f1d1095d270f54f28866027ea95f63a6e5715618c99e7e08f17948a1e8c65d0f223eb8346f3a9ca6c43ab9669df0c286376cb610b5a073c5371c2c168935bc1a82162a0e476196e2da58d90892d41a9a9ba1d51aea5d2e8fe1d3554036501ad561776fe8203c0febc1bc49c023b4f2095f7ab99dc9fa4a09c70050ea6c1a6f93d04e66dace55265374deb889c25cf2a56f3cd964ae8d3ac45ee95781743bf701a13b4cb3c6948375a09050916d900fb3d0811a077bf61fad07727b53c2564dfe216679c8ff255da315e1e475b866a908dbbbd515d620c7182b849e8863869ddd808902283f89ce762a42304280edf68f9a76b67fcb61a7a54cda8575384ef380b24d73b1e75b50a6174ae6ab664e0a75f91d4acd60402170a2bf967c9862f0ae40ea80f4d02e46dfc9d22f4c5d3358a3a741b39930b017558af7a8c9e268f37a00cd48b6a5afe6dc0785efacd85eacbf38ff3691f503bd723796d7a2b7a5505471c0a5dfa4b6eec5f56d4b7e4805da697d7a7e2878d717e21d742f9699ac0aa802f8b8c774afd950836e8ec22a520412abd775c4619aafa9dd53a9dba31f385d121f23710baecf82aadad99712d54fbbff07e19d618663216ebf13dd355389ed70ab297bb2e8f14c44d9db5aed00f4272956f940c814e02c329cac3b33e110f7b260c8b643c9225c725f638d415096330c953d46a6e6901548de47f050cea00921c406ed69b8affa8c49e5126b09b7407f1fe39e6c3a67ff985f44b4068d17ca2211a8e6004ac2ce57b738ea79b47a79212fe05f59fc333dfea5b973e0189f8711d2ac25b7da815377b5e0b79bb57edfeb0b3e249284b093f937eb3c8cd5dfc470e799e62210ee5038ca82113eb298d0a91ed7265809b0e3c41842327227bca7e179281876a2d730f88bfcde6a261d12b63b325fc4b8f8a07deaab29e073e24612e721bd4dfbc7a76064f51b97cb8c9aadaebc2219602c4018e412558f0c92cf50f0300b41c3af93217af662fae6c20aba5c5de25678a70e7548eed1465315e04260f3dcd91a642d67ec3157acf46ef336e01231612fd33a33d547b352838ff602202827e24bf1d82e874c4b2e56d6da2211d4f7ca4affadd0e17c9939bae8c861988b22b4b7bb9ef9dffa40b919b3a3182f30d07eb113eb168bd5c30bd8b206a032495cd76b9b5ded766b724b3e8a955b114b6bc8eb936462efea06162c87c2c1caaf7adc04449a6a314f788d3f2e7ad03a89388119434eb73467f6a22898b762192c947c632f83b1d839ac813849f7f22884a21461081a6d636bc2c044241732a226e71541009557abe207a50bb07ee6e5318c4ee2f10bb0bcda8e4fe00e6b71fc41469002dd8f72c043824f87eeaab33b31ac8effef573e9f15bb24f1af840107e0034eaaa8cce6143056e561c708175ebd2bd2b01f7204024529b36328ec107e1079b23231ca0576310db1dcddae6dfb2272b968d23ce655bfaa3b5b578bb051aa676102be135cff656a57bf369ca177e279e14f723c6d567cfa0cce54170301665691ed411acc68341b6e22d99e80c111504fb6e8fa565540666a6c139f21693e4c8d39fee103747a085926e9c36d672e0950b53e0168b556f7cb1a672ec12a0aa0eab6371061b8532a895927a22ff8cd0d1b0f4c4daa831cfdb62551d422abedad3879ba0e5d639f4ea58196f23087af2ffd496a7572303590001f57c652e0d004ca35307c2f8c99a335d6378a3770e5dea4aeea8e5a4493369f406fbdd1bb85993f3834486eb72a18278e27c76aa92986a4b9285143493f120104d628016e1f540e94796eb5b0014ee099328f84ceb614add86ccfbc550f7600f3413651c6d6c29222e50e8a15edef04969562fdbee5ffa843960d92d565d380cbe1def21d8212fb0ef618b09a78a0d3030ea274197521f239b7b82874d575222ec0ba81ee82dca840e340012136ef0b243eba0e650058aac4a5fc88ad5327ad848ecc710d71beeb1dfa88fb932d6125e181271335a9083d7fc6e13cdb92d1ea5b3d60912591a5d8eab8b8db19ca4336fc32c3147ffe8088684fdcaebf5f634cf5e1eab1b68a4d96dbdaa0afd40748e0b4a0ebfba0a627f30c9dbc621e727f7bef5e78e245f34dcd0a1828753e5723eaa14bb4313dbe15d5e76965f8bb765910f417ae403a931a5b653d4fbe31332fd04fc6e69626fb834cda6fe48674b98219ec25e212df043b86786dd8b06d34b0bb7e6cf4c5bbaa01158fc330b05fcee8b4fb9eed20ead836e6b8cdbc6cbc06457ac64cf3a72283181e35a9657ba381ed47169ef3f178a2f40907c36a2de63a99c8cbe92a23d9b10305c3003f5341ef1f9bdef0a99000586c10ea5861a049a13f7c69b0bf6526387aedb3952554218cb5ae0266b3d17107b45cca47a5078acf00a36247bd987db1d331c585953c27b3e7b68e34c5c086f1e38aef9ef67f6bd98ba574b8534c2f71e4ec038565f0b24c6faeeff168926ae5209a04acf180a8c7481af21deeb7a02886f61625e734604feae11bb965306c08ab20127db181eb638f57097f85600f251529faea4299afb46dc539ed447212bbf694211232606e3fc74627321d1188ba6d02f9e8269dd81843182e41575113b3b7b2c3b1a8698546d3aec50985c37a3184edcc69efc421fd3e4f939fde4182255831a27d9b5d835ed40def306d138225b855aa0b81e5707438d4cf7408725dd70cb357f50ec938767a6216c3c45ef2034d3fbc3c8555a67b7dc78b47d8628fd188cef3d2b8dfbf4fc38ef79f480b2efd8c4adfd46fb6205e251212ebaf31e70b2d8a82ef799db3afddc46ae635734ac0c700fe84891c053e63156981b0b0fdba632fd2f3c239ee4bd04254130f5d66e68515143c5f4d6f2b29e17bcb1a303aa93928c09c5312a4d16946121da973879c2af82cf71b14a2ecbeda0e5ec8b02462777d72e300a2f695fe1e4440becc12fea0e2ca14ff1862931e8175d31cb1c9f8844549a6ea22385d3a4f60cd14fd6425aec8890bde9221d42f60e54bca41eea53a6a00c6fe7b66f129dec025d3c05b1a7661322b803efc62b123918b9700799cf50fecd11f0787f2807bf01fbec1f9774eb693afa70d652dd59d292b74809f0b45fd23a36228017f8cfaffd39368d2317dfa86d5eeefabb69e13c7d7ca5128829e70548ddef6f0d1230f3674f752890c754e20a858ff699eb425595132818423036c330b0a4c86f547594d8e34b5abacc9933308d03ff4b5307c9f44b721d9e1d6655d4140a8adca0117a6ac07565e7e8887975eb3965583949cd93c18900bb301068bcbc64aa85a14a8ef8365ea3f57bbe465f80ed8c039a04a7c9e5da0e09ec9c44885c8ab72021e4f8f6c388240c577957dbdd7f64e929aa54c2960a071e8b4cd84f4f659b95605e75c42ca42547f2462b36f75b57972e6f9859d45f067723d028b166e84ad2a5d9921bf762376c3e90ae6b17bd87c27e0fc007a77d1f5f7d8b0028999ef82f084a250d8562793aa0ed27644ad82feb94e80d948a6f09feb51fd95f0632eb2b356c130b50c4f70fb0f967cb7eeddb222c546a5efb49050476a0d41bb023dc5d85b1cd7776f9a2c3c792be49704ef8e0bb09b9e6913728a0b8490ce3a1277aaa62aab1f07ebb41c1f395c34eb9e983884e8dd953bf4a0f0e353000cb746ce229ae769ee10c47f15ec40591d589d798d0c2bd2655751106774bd58d59720491a086b4721fe745209895969578d037e71ae982f8a9320322a859297e7a85a2002ebe2e883aaad92af39668cd0798c08b7eddd7d503b24b1ae2daa5c4eb25393ee6ebf4008bc4e0c072ca90b67a65c586fcd5550b29423f093d0da983505afaafb1eb0771e8e9e1cb3539fbe61f5efca2d4860b1fa1b36121e5effbfaf7611b946208ea448d990b3cfdaf50b40063af0b7cccab49f5c86b16506192e554f89afa326ed0528f6aa248eba2da1825d58628355393113a8fee286a02b1c4a452f8f15011bf27dfa4fd6832f09725a7219294e11954b957f9221166c24763a83391287fc3eefe14933c5c14f4c7d6937f7b75542347d04045a110890ee0f78b861b3af29a624e25b367017d565f468cddce5b7f0cf3c35939a710bf47754178cfcde50bc00ad8afdeb5f65ac80bf3130f8f916859fdd0d676b51a19030af15d64ddc9aefd991cd5cfa036cb80826b4373b9d7bcc892d88b7dc91054be1686426a50d43560b6f5a738e11ac395dab9fe8c808f8f8ceef391350406b72a33744c6e8340275bc6ba889bb2a39e4e44a5ec3608ba7e9ff3169958a41fdf27650f39023fd280fbcd8a14f01e01129e55d1f39690748e99810326fdcd20e052a932671c905b1eaa5c122a28ec8ee55385d4230903c923a10347dfdee01e17b2763b6a7060a5b8096bdf84673c406e1bbeb5fba30c109497c0aa3ce4d91814c363d790b240b21d0c46bca0a903c5fee1e5fd6e2278a36d7209bff11e701244171b2ba258a8c4b611fe9d842c33b90dc2bb751a4c9834b94ee633ff2f0c17e99d90e05e03e67dcc81f0482fe23b3edcf8af33abbd48817fcd2f01e5cbad02174e2613f854965fff32fcfe1b2ceb878c5e8451dd0aa5d75a96db1423038d224a2a1f5665b42ccf0cd2009d22d026f92aed1329fa0eeeac0bd789e8a094c82617ebca343afad708cbf7746a1a7b486be884219e41fc54bc92afa7c4fde27204e3b778f1434022e9a0f5af3cb796f94594c41fe3d1343ce8e95cfa3405e76024841490ba15b8d6e571c78b5fa9bdb4f42cf6939f0ca766f8a33f9af1388e9f0cb4e92a32b3f70b729d395a955e2080b6f0ef0d26e8692148295a7b56b5f6a31596ef09d919d4e9ff98704206e61a8dc0a9ce4662eaf6a10878f3180ecd6bcb0093e2dacd06fe78b044dd4e2f973281e38c793a05bc453015dd5d99c1adfa70234a32f957d93338fde57563ebaf78674ac0ecfd3bfe0a8293a8222791613d2c2ffeacc050d243fae78fecd4172ef8ac8d65605f37137ac2d95706f7fd8f331204bbed683e9949fba2e4bf9941b4939ee707c3973843138bebfee49358309dd60265751d08c2e979f56c6bd140921e7a66e34962c2e608d7326c40f95b797636285580e22fa6e96ffbdfa09b8be7d9c5def6bb69b24b832097fc8501a8ca707c0b7f05f0505eb62b7964b96d0c4913735b728fa4baf217082e8f47e2f582872412c7a05dcc806ce1d8cddcca1d989321c22e660aeba3d8945d7f932b6de9e6591b97a10e4e7f2ac05c0135756d3e162058d1b9e577760bfa12f779dc1b21c04300b8aa9e06aa51ffdcd11ff50c67a307276719bc15611fd86c8098faf7c829da2c4b1f4eb53f6fb595e1961d38512f3ce9827b9e4822b90fe6e5c0c52672cc1d012e591b62f714b671321c5ce756f826cdc2bd630a9a79272f7aa02baf56779d25d42ac69b671532b1d51d23e7c79160bc322015ca838b4e674eea5167aba83c218ea51067f7176b027556fd11238b4bcf8865d89a9cd0bb3e4f43477398ccf22aa043b2ff7867dea2472de15e42e20bfccd0fd02248e6f1ebf7f95edc2eb2312c0a6b7ad4f50c3bf03555011fdf65cd0e2fa85d3bb7df6dd924e7bb5794985118a1d10561c8f03828a912b04d1e50f8bde69235134a4d4ae2da48c118a5dce12e703a9115055cdecba78a75a6d10a5e56613e9dc3efd226cd0756e592f880642c9974ff67e8a91633f52d02e73ef44399b35cab4796c37156f0f9800b7a88660345dcbf63e44f1fa2d1f8eab547a67f70db529978f40217b5422eed2588a4ee300457f11907f9415e349dad8cb86d9910eebe588cc8c0cc798a522e39a669031bcc7172286c9537143a92c965481a89f3c4e860d5ca9824175d9c7a597ee7c61c115ec99fde4909f191e31613746673c5507ab6e01436e6fe5f07d8a929fef51ff7676cbb3def22e44a545acbb9aaee1773f706fc8684a7b465d2f92f8af7dc0a1800216db2c08004b42287c6df656f6b5dedaae8e1b9013c94532463df05952bbf330909de4d033727d32480cae73d2a2e9e50326fc605ae30c0c9f011128b1a678d3619cafef8cf5d5e07d61a849c05e91e9ce8290b8e0ee69c705736ed81117bbbb0fad9bfc93fbeda8ec8b9464853ee161b5fa9b5e954c36d10f7d2e01d0837f15100dc35eb17b2dad7ccd87b482048827c1716d510244380ac5b0934e3124b0552ca7a5902213329c982bf38782b0aa337a618d658aec9d01e4885977f1514eb3c815c8ac482050a2d19638a584180f196c16339e4652b1b7f1ad870891d63a0420da9390f9dc04588fdfb066906fbeda08ee28cf7f06541519f991aa7125c2099036a614739e41d2ee4266b3faec273304a8a7fb09758a64cdae16d281d06427553cb1acf30f1603889e575889f826a832acc44180f68c230647d971413a392f060a527885509f15ad448f8aece5aadc9836862138a8b6d3b52c33dd41faa36de1e5f4355825cc13a32d55f9d84a7337b691d12ab95d7eddcdd3f72a22ee84a7b8932c0aec98b47176220beadc273414c3a7d739e598b932603544fab03f191cf63a65ae49a832011f34c94d75bb99361b17399b27d77231128adfb33018fd3f19e9f95a38b4f4296e1b8ac28f6ee41814be442b889ffd99b621ce5bf1bf69588a55456a7f9911222a4825f68d22106aaf2ee07891bbcae65d952e75b2ce90f558af3f890bff0115d005d1e5f714935cb35aa8e12c3b0f194f53ba20427e5d13527c1a9f7563f61ae1f59a6dc16f2cca1df47ba4d47ad490a54d1a9af10e1bfddf43ad707f810b5b9b48a91b16d892c73403c3f5cd6670bf79e355e1d1cb721ea112226ce6a6d52283c887da649f153a7790ec04f440176987419b5421b7c694c646cbc5618e0f2e9d9ca5a10be1c9e007d519aebc3fcaa904cf4028d0f15c92504ec8655c6f5ec0bdac134b4194ab85c505cca657e295d2041c68be114b83a369cc7707cd41264238945dcc4d2581939579ebdc673ae8a5ec925776e28341d66f9f6eae02473c10b21c2f682ba22fd78f1b25895ac2adad8511422110bdfcc50e8b7e10c22d1061ca72d0945d2ae76eb874cca31ae41ce92e335e6732e0ecb8257b64e85774c95c601b69ccdd6cbdb5bce2bdb755e0f4309e735725f", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e94000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000dfa79688268e94f45000000000000000000000000000000000000000000000003b27d8c4969fe006f000000000000000000000000000000000000000000000008596197ae31284d9a00000000000000000000000000000000000000000000000000025071b1b78e460000000000000000000000000000000000000000000000049f29ba73988453810000000000000000000000000000000000000000000000037166cb36624c1ddc000000000000000000000000000000000000000000000007d050eee5ee699fe8000000000000000000000000000000000000000000000000000258bb7095c7a3000000000000000000000000000000000000000000000005704f0d79a04cd41f000000000000000000000000000000000000000000000001cc8608e7634392ef00000000000000000000000000000000000000000000000af88bb11f4b13a3c200000000000000000000000000000000000000000000000000029db74f7fe7cd00000000000000000000000000000000000000000000000f4a5900047ae9ba6400000000000000000000000000000000000000000000000a2f7b686593ac5c9b000000000000000000000000000000000000000000000006e5e184d039f43481000000000000000000000000000000000000000000000000000265f972d900ce28738b09735682ead7a4c64799135201b3899d799236623c3b56cde6eeffc14c0df0717f0a39df3d76c516e8bf543821b9f4ba63b14188d420b92f5ed32350042061c4c31ac25d95dc3913a7a4720dcbd0d2b502eea4245f0e8e0b648eb3664f0ffcf0587456eae3287e653a00e8edaaebecd3e65d8ec98782517ef25ca5f5ed17e994084f3bba80177c052107cd4673933d4d7d498712c3c1a765d4ddd106cf23121b833a7165f5c9a7cbae738a5033ca28022a37b8399326468eccd785a1650e921736cbaea5f086a1fc0b8d34c9b3cf259d4260e3a0fb734e9ba562371b5d22be915dc76a01693899c6a34254a2ee1d2034d4b4afb734de2afae62fe3949727c3b05c1e3ca04b6abb43df9172b580ce32a1ea52c6a13bdabefe41b291b61b0acf905985d7fe37c2a12230fc1fe3c698d1495d937e0315f8ebc4f08b81f07b054fd658b021f59f1486e8105180cf9ea58448782cea621e76ef6e2b502df53c097f54506c667839a3fd6816ce0201339146d1334deb91f8ea6a14405ad6153a182d352d88c84c0f0d3446535350ae13ab7965e65605009887d7909a2fa1d42e2896551a0829706edd47b1b5786e28188506120cab5c15268750c76c2282e001032b6979a88d90b20a1db9d030107ffc0677ebcb6777c7f4ca2ee0510182362e2657252207babe037b83a8d8b3dc52cc67e771540a593d889433e9cb38eec2d71d52810b29a729959f506e4ee054bd6382f9acfcfa7c288777d3a5f4797e9342229726d66b20423a7f33a255cc294690bea014d9afb0331f91401058a271c34d2ab341191877abb7207f8754970389d7dfcfa00ec4ffaaeb0524dd716cfe7cb11e9223897133507d5227ce3ce11671f837ab1ef85d0aa6e308de0ecae5e49f712bf468ea81ea81e5527ee9e0f1a6128b1d1d32c56b82e3da07269e74c46c9978078a4fc2c3ec815158339b79250151302bb388375cdeada8e2669ddf041f19621eabb305f4d3270b826fa6ed2f25db9ffe39fc47e81839abfbecd91abc210133053d60869e919a115fda8d5046e59bfb2f874261d17b698de9e524a7145be6c521e3e2c29389e805e67940c921c0dd97b619b10e3d3e1822e3b2906d448ec85a031c38b5d1f34c2d4b5b7d5b6360c16fc3f097a28fc83c272b1fce5a6398bbe20a3997e59fc3a0e2317de98fdb917166780523993068ec1cdcd514df1fe9083f1991ee90b63c34c18b2639cc3c3df351ba4b8ee53f5663219c845085116bb81c1639cac4dda73a16cc4d7f9e8e607200c85a8e0fad44becf3891ffe93aa770132a39310b85cbbff14f0bce323cc23f445f6a258707355b3779232a642eff4b1a09e4b99928bfd02b699bf248fd01e881d98aef5772a6043cd5619e1a61af8b3303c7e35dd434b2854019587951ffb7e885f616ef360789979c7851ff5bb479421f42bb812a236b6fca6f7ded6823a28d36810b5eac6732fc2ad4410f715951cb2b5e09eda26e7793afd5c7c865de1570c4e72d9a88ce4b31a88264717a070d802a817a31b2c84d4c1f13a918c3a73c88d8a67f6c81fcf41ecd278894e88485c5062a3ef24ec877338c2cbafd1e61205fa95e2b93c59326cbf8f6468453a1e85e054db79eff791b9a1cb823dd9566f06129dce2b1f1c15e9c6d878629d40f61512eced624da055ff54fd71cf3520e5a759b33d31ce3d711c6e1163e3645a725960c4b1b6a5254cb9b135176e02075b45e9e1ea54e21b1a420b1414abc132d02be133cc2dfd9cdbf2a0a44fd11b51779483b4052c658b345b31ba93b918522167102cd4824015fe51e5b84a6fb335ac4842f9ed11d6f5e69b4d8dd8ba64e0f36270f78b42c71621f1501e1ba49fa4e661de16cb8dd3c698093969b8964809106412790264f0ae9b8007b76649a949b69ac1d6ec2a955902ececb7d33336b485a7c2c3c6a1fadf0fd121f79fface6cdf947dae0f776d069829fa900f514ece4141616bbbedf7de53e30cdfab6d5619028b321d44e93184fdf01e7ab3f5a4424386c23b8e5447413e6639c215e2f7bacbbe08458fe90fcff82f4dc73b1b9fa944e91108a5c28ea9df2a9e1b17874a186d1d5301848073af15ed52a4836a71b9576fb284357773419bdaa332b8130670664dea49b96012f02b091e273421d579b8b7c0db2b2715658dfaf910c32aab84a6eb1daa3f8c4c72cd2b43abc44cdb4aa8a0b0dc67c649a6c3b9cdcbea178401c0abcb272d8d5240a492956cdb2e4a3de78841ec42a7c9a5ab3b10d4264cf887b1c81451ebfb5e4277942e5736a7049bc071c168f11d6111ca6731a245c4f6f0aa9acba6d334d552193f8453f06e454c0f1b30e3264e919b2c667507684454b0045f26da17f27caf045c7b4d2f6be92bfa160277b4cf55287f67b9560a68e53fde3b7b85ee9043123cd7039658e7f874c136d1a2c26f2deaf00a930cd611ae4ca28ec30d65792e9493c2175e557b21e31a5102a8f42f94c87e31eb528d7a5fd4ca83ac3a1b62ed65113bf25e5e0a82dd2cae52cba89c170eaf2134fd4b818630ab330f68c040bca7285e619b2d493d713fbfe079f72b0b80fe7f56ea6eb5b20bf7990fa72a7f26191f159a2eb29d01801330d26bad38bdbd921c0e68d44a56c432329d5477f1e52f1d720e9dfb09f24fcc2f610e4303caced66f68ebcb8d876ec6f7c1904ab11800505a020eb41124069deb92d748417971506e406a675a65eebf63796897742b6080c037c097e742a446ed528783529668433efa5bade2e3f9052c11418f54b8bac5ae047f5fd30ba9e031f2bf38e36125c725b69365847ab2eaffb80c917ac7d3a97a47c1a1a9950db209d0326fe9bb980be5ef2bf21a3e85f236f53aa3843581e6e49d3b4451db82ff50218cdf7411b81f746a1086be6f27cffbf9f2352b4fca25777ae421cb673afe74d276522587e772895b53893a247b5bfb427dacfe13c2992700becb2313d3dc00f1876d430708da7298c907dff0b5984eafba5b26cd6fdfd59363b0bc511769da02db19b422e86e8e4003ad6554001fd10fd87d5f32af8a9d13f23bbe12525038a1a7a3867ea9741941c29e3aaa8491855617b16f140ded2b19a0b03cc4a4cd74c20f4111cdff9aaca53255e23c1c6412be63bccc4930ef87a63fb8e59411f56d4214b5fdb1a07fb3d5e91b25b3df5df862ad9cd72d3a24325c122801358aa149d2c03a7e24a687a2135762b2c1108cf1432e73f5ebb8e20a552ad92a91dbb185f11f9ede94d1ecebb9b30bc4c1a7744e2d121a63bb8fb54c2a83dcb8bb61cd6b600571edfb0c5ae31403c4fdebd9bfba727fe0422077b4bc1b660ad5ec5db84b7279d5845420a92bc08b2e5698b81383cc1490904ea5f20406d04b670ac20b49919e8beffaf81535d507d4ca8b08778bb8b791d016fb0c935e0ad7f1c6146cc4a262ce42077ad7d82e2e3848618c2e9514a3422581329e323cfb5e6153a6131990a4326ca63611c14ab69058cdfe7289463baabde69bfb64c895637ad70f47acb101d099896c7ba7b7aaeb2292c376efdfe9278aeabc23363ec0d2c0fad2cf14325801e832e9d2edeffbe63a053f8d6ac11a13118adbe3cb26f66bdc367bdf2a11bf5271e7b3828c52104b02b5f157695823763e7093e3e9e680921cd0c3dce450f4251845969a5c04cfa21f68c982ad2303130bdb22134d6210746c76817e25a249c64fff8b9d2e3651db73d13eea4360f99ebea5b17fd312fee8b1117e4967004fcf53c8939497fd46ee37dc67a58480fa1d8f2cba62cd1a04755796bdb1dbd0625d390458c05bc57aa6cec0ecf86e5b90d7d7484ce043ba332d42367e4b5310c45f065d59ed77cdf230d709944f6dab4c7782c82a2ce64161caa970c2c071c11e59f3300de1c8e44688c10c9214d8ee5d1402f31710c6c033adcef7c521b550cfbf8b66cb58eaf5b4f600593e7a7337c5b37df382e5df9bb7da69acd0935db2f71443d93c858a05143b20f06298edcc053d6f52755f8791f02c80856e1f55b0eab451543b5001697d60235605a12ca6377956f5161eabb574ea08f2741d0501dc63400f0e57099a68241c1d29056336dd70b0ed371da2615573480f10f933212ec6e0b1f25cbfe5ab5958a232a3c6555bc5d9a88ca2a5433eeb71500a178dc1003e570f070517a79e4ad9d72892bf563aebc21f65a15617fba60ab62749c622bb9822e4621ceeae8fc692ec1384c31b3fa7f85ea545106bbd4f0c8b5b03a6a0f64ca4fc38aaf821910592887b951fe39a88fed773f3c32e9d3afc97ee079d406d44e6772130b54178d8e84cf33390e8b880c554746bf355472185cbff9ba4c218facbf53bb5db501c6c583ac7df90901c1a4c7bfad4dc5a1f811986294c3e30b018f42f8d486433a79cebc05ab5a4861a0a19f413346be6108d93bbb7ab24d0f1e374662c55fbd4000b52009915ae41ace2fd2798513822da5c47b69c627522fa60c93b96255feeb8fead0646f6c03c55bdf0ce8c90d8a4ced28275e6005101dff152a15dedc7a5077b8a6a24fb6ba47511dcf942b097d341439041c53dd82030dff114cb5c1acff5f422b9f9157306540025097b5ca9666848657a1bdd8ac035532d8747b5182f12d2d76caa80b6ef5f2c23ed91c230c03f3c38b667fe41f03c5292df7fcc0485405a81a40666c864a1c0bd0f9797d7f00211a2a4d7073be1a8cc3df21420e1ac68e87a2eb75b32473cae6554a8c5118662b250f3c8e00ae2e2973ca4bf0d376bfeeaabf8f7b5c4eb4761a0b51fc018d7a205f02979d60c2027172019511cbaf761d504bfb9dfc8e01e45cd38b4e10103c13b2146174d8fb073f021f5d8fb8369b690f7a5df4044a2acc9e308e6165e6f7f5ea5d0d5f695a201986328ac16e02384a0f49de5929baa1956458ee19a901e8fbe31ecfa803bd2954fe80592a60132ae23e7c8d4d79dca4d60db2dbfe343b01cbab95a814deef0d2f09686caba0a5e8714860f809bff73ea57ed27ff26c921247633ff0f53d010898444c0522085bf95583c6bc1ea11245794f6442c79c3467afd6de0e938b9905fee8793edd702dfee158d66580b78e7124a0aea44968f9ded2dd0500d8263218735ffa49cc44890afd2777727aa4f9895a7039122d6e333b3520dde3f6096c29ec16859e98a504c88d771cc47aec7ff40a9e4f201ca83dfc7574e01261a9ea214114b3db1469a1cf55002bbd38d0ea429b1e24004d7677a57f16b4b0cbc25803108c1d04f5c0c3ef669689f62188b71909f1c11d95d514ca3a78edc8a7976422a8c196a42444ac4076ff06e369f839e38244111f04d4f74cc3ca2d635bc3001c60ee36b09471a5ed2d5db3a067183c50ddd40f2d74469507e949541d101d7e16c41645a3fc37a00b4708cb588611fe15d807d9861ea110e6883f4df31cc8ec278d447019f5d65f772020851c65d6a10ea601c7c4bcf78e58fc905b2c664f0d2c457a65f6424d6739ccaefc361f1e700d1735c61328bc29e75cb0d76b1c013910c82a1c8b34214adff97e9d94943f069509a46742bcd835da11ebb9b440dd3624c70366a5708a3a10b9688401e4250bf48266aa77d0ec6e5403d5db41f718f30f947098f457716160288bdb4721cea1adc69d2745d7aefc57eafbd2e901013b2ccf7ff182845d214e0fbe248a08364934d0ff522a9fd3005df960972ab255052930025839cab3bfbb81d5127ddf6fa01a61a613a6e8f128a85004743292f3cd2c1792d9ba72f886ed7a121297461743136ce348913ca557297019e2a3aec0a51922836c33d87bb152b06b9b9f3bbd144c703eb94cdf020568660d841a255e5d276bde9c1bf661ff908ef89874dde149fc4dceb010e3a6a382e292716cb4b8d916263774fee4ab4ba83a3ebf0b1064ebd75b9ae887644471f86a6bf8221107cc078182f2a821d52e0cfec07bc6657e50526ef9fd0048ec552d0808a742742aa625f90291e0ca4e5a4bd0c2588f4f2072da4cbc09e48791ccc16cea22ca882cd00d0381fb3133577f3d82a0a0bdcb0db0e88fe939063377ee25026eaabd38be1902111b320f710bb3503857b61c13fed36475db6ed1080063626c619d22f9b0932aa34375d85de0439ab7c194f504bc2523abd9627a8eb0c3ddfd751df66f5c95293d54b5e457bb7a9f8362a8f0e2a8acc4586c5f9109067fce81c82242a2eb5f2558d88d1bb575b0651061ce1f9bc8c75a95c5f785ef7933948632c1cb5b43592f88a62189b6ddad9274e56b188dcf13db455a4801c175f2bf802dd46ce5aae405421fc32aa47ef04f6c1ae84584ad7f02e065e6a7cfe8d1854a0f5431c7dadb0437a9c689c31a4cea1d48dc8bc6b635382ab3f7dcab912b6169fa9550900047047ef7b1b8393d511c82c6551d90fecf2662f336597b24113124e3d13b21b27b05f81ed1c2b356defe173c7cb7e4a5d33e4dda4cf7020e7fb85bf2893696a1a3238660d480aafbb0c4271397203e1a80122314f7b9ad807960830b0cacf78e1b1d45f76d3dce3e1db3071e9055eddca41a84da428fd19241adbc656e5cf7255b0cf5ad55a164b6805f639c3f7202975845aaa64ec03b57fa721daf278099b20c04630ee5a1d2cf8aa318da7097dfc780e843e1e2b8d7a550db5cad5d2ad4cfbf2e115682cc648ca983330ffb7283318b6311873be9977637d3acc6b2eb932bd00f387b1c9aed7bf928c15e9af4b7c165e4b59cc38d8e190b8b3a81d1c6d9f83725e0695fb46317bebde0aebedd01b2ed491ce402d501aefc8f4c1c293705be6b000354686877258d5ec4bec33fe416c64ce5925bc9d9a537b439250967bf458702d23d65decbeb6752b51e297d25b2432a6f5b64c0773b205c4e1446b087a44324cce538d0ae9959129c92b2240f863c773941aa0d1c503d20b91f430a467b4f2bbe0a36f9facb42624703f09f4288e1c7b74a9cbc5b2bdaf212a49f219b618c2580560061039f9eb6c52b7688ff624844a0a4acda0f1db128eef1bd0259ac3c176b1db37e377cb9d25a85c75ba3643e894de5bd3e998a4160837cd75f1cdc723038cec04a4853a6ba6c62cd4918f1632ac30de6ed5c0f8b0a68398ee741e60f184ea704315635056068c59f3feb2e28bba86ca05e77abe071bf4d179a0e9e2d0d2bcc51f8249afca59bf04cfb916937a0183917178db300a3d477e6c83f018f0db99b774c888c7019932c91882095d48b2c84a5004642e7ab200a22e90de77515c4e35e9d6ec6d71aba421ab36b30e7328fa15dd936f1c740838181ff349e122ffeb6ee1157415c7f104ac0f38ed2cfad62acb25a58a89eea6a5f66b4bf392520de5b7a89e66f24a828b7141b01fa5c1067327831d040d0e7a07f565528f2ef250121b6f9948bb2f3f2cb4099853f11073ea5173ed10bf2cd22f90108fc7c4c231b03d6f484e3bf256fd3d4f82388d90090764a9b6eaf6cb2cc2806c5fff469294e11a6fafce22457c6817315bc429ad19853b7957af2af6c3389e10c5a105a2e8d982fe73afb4addc3aa9b344fabc343c6438c8a59098f5936add0947217cc2ab165ac1555e0264108944578a3738595d56b7e274644af28a52c992a33209c05dfa9b8864f3155f77a4b18eb1eb793f4721180a337c7fb5a0b499ea96afd5d00044ba5da625f9a36bc38107cbedf12e0a51a8a2f59f2096e0d20e065018da92bc0a7112a7e48a4444a728d936338120a122db00a096cb486e3b67164120ed11205e12b3245fff17ccc63ec28b2e2a1f3d2bc7c8e7403e0c9c5d5a1514a2b900797eb3f3ea1748dfe9e23cad65e839d8e3074d0fd24a1bec13252b57f821f9d068c1cc3c26382875bc0d27da9d3bed3fdfed96ce2b7ddd3446e35d91b31d301164e3aaaaa7534f8ad4eb17b0621e6e5c876d84fa1937a023d2c29e37bb14c9202c71cc7c96f699ecaacae99aa36e15dd8055793bc6276958cbcad551d2448d1199499f2720dc367e892cbec806b3c9459f49f22dfa2b990fc8282a4ce499ba20799ba426355a8ef8dd0fd899bcfc06372d212c6ac1f63c8bd5e6914b18cbcb61704504f2e1e2ed6d86e2da36bbac86aeca425a6720cb4facfb7fd429b85ad57032d86246b281117624b1b92e7da1c0d27bbbff6a32918fc2f5945cd0b4a0ae312ce4799fc5d07c7180005bab2956d23025adfa9e3e37b7a6166084eaec7740518fa072d7ef56b4c1f8aa21142872240e2dbebec78db08384dac3cf7c36628851830f724379349291aedfc0f9cf05c92d4af5be0a50f586929489f98bc520eb906de5f1f41ebe26c58492fcf53552fa0d1df87ab01ecf27efd93652d85bf06180c0bd83850c4ddf904a68bb0f3a24d6703fbb5e7e3e3d58561e1f21783729acc1c3d2d9b96213fba91b08d5a06eca9adb713d1636c1c20ee1c6ff40a786694870e24656dcaccb56da1ff15761c4128774105f4869db44cd54a07414e76bae9ab2bd6f76e121840d8e8f87364deaa979ff118965f2ccb1ef97b3e45187af7eee1034b6d88504c0eb3f27f35f0f36cced25475b2f62df8cceedd3f4a9c7808fe41219bca2c439d0b29739bea7622a1d7c44d8a1de6d59c63b7ab55283f5df05cb70692e71c660ed45d49f1fb0790bdf274bb6cfadb9fe7d0c436f8f784478cc8df189e594ea67b7432713bf22c1199d90e0043695858aa46cccc397e528741c6b52e19c9ff45986020241cec343c8d8f74989c21f2b86e94a704335942c967bc9c032ae6b105131dbd3e550889cd96d3577f80542ab10e53487ebad39ca428ecbb236b4ac29357495fdbcd24b194b37aef7798539cb7407fc2d16cc02647616fda1bcd71bbaf8633ca0fcd9bb267a051933c14313c8a09c665da7ab26b142e9d811d717384396cecc500f378df1db9683fda74d5bc2c4e78fa1a89f7cf4975ad69066156264ceee9b8b8926b5a7105c90889998882ac9420f532112a6f1f2f2b9c023a3ed64af572f19d017aa3ef6afaab087d3bd768155bd3df0fc9f7f822ca88009d54f9ab76967e873ce437bd0f7e3195cb48599df46f54ef9d2e424a93f59213e02a423870816fe39839111cf60a4d2ec4acd8e43863cecead8f660361b8a903bb39e0d785dcdfdbe89b85c713b3355eee749fb6027ef71e9984d38806510f18a4b230924b0b848a291f8e62922077c1034005c5ee611f550a746bba75289e04ea00ca60efb7b8587281ce5223eaaf8a07771df77a7dfee0d61b4d1e01602517cdae4f6c4ad7205b5c2d781c36bb7f9c36d3a1db2c4eec78e001dc931594700617d097a075cf9b4e688aff67aeef2b5befdca4f21e388b8c655c2b55c24e511251c2d400d40a3946224c052f1ce75a4442306569e701bcc086439a62f9e04c0758fd7bda12e1ce40b209d30d67f2f003fea544afaa1d0420f6608116269508081983a5d8a574b216c19749a1472029898399220ee4a6506f95e7c5787f5b9925e200ee2c2cf08cf40b446659aa9b72d867e89de012b1336c390f8884c2087e09f19183af33cab63051be64938e007258d7bc38158dc0e80b4f2b76d13813ba2926c0c2da99395ffbf9e8cd65c95721cffa67de1b3aacb1e40f9a75ae46d9a11e06d148dfe7adc51debf3a5ed2958ac53f09e30b368648762c53d629ea154aa10cfdd10faa8de5faa738ed3d7bded2740c0187ffd6fb2f51ae27931572d853d27b75276de25a8deb23b075f1581f71d8b45f9525f54c924a3057476123f900f1f8fc748b50883c13789486e4a0750c4c2b32d747542761d6b41b5afdb17086b17b7f3f2125e5df91bd8ff04ec54b3b732214ab81bf6e21560db4780f1f0ce7329d6540a217743efe23cd1c6694b52623718e1478973ba5f76afd37d376b8930082c5adf2d53ad44dd629106b3c3b3ef24829f1570d6fe0e6bc673a33d3f233406fbe881c13361f5495c94f2d43887657f474a5ca6ebdad16ee92ebf3755c619252bb90cee7c61ef1b3729012c54101ff03af47b6444e81ace561d759d309b050a82f6e71cfd7854a68ef7452aa7e48f7c5ae0d48b5f2d54902687a8f0bb78852bbcb6b17cc2f8cac0ec1526e2a60015d3efd9a0426d82aeee4260b71813bde80392ede8d86bf0d16af0977963ce5a573f7d561fcd20ab05e2db8ea7ccb334ab0307670beb2978093fdf7001199dd0d72b0b7752ecfb91da30847c8243072a7c2f199d811b6a6c1635c6ec835b05fd77235cb3d8451194f699293e70133d756f2c9881849ce8eae584597a83c53a47450a578d88f75f6982d17ddea04e9b9ed5095939009049cfc7cd6cf9f412a3cde99720adb91ee077da2aeafbc7a033044a0b672f1ad786fbd543528c38703fb4a37c1db9a2674feabf00ff33a5ceb01bab1aa064e846cd5d748df1714875b6a62c9dfe562519ab164540c0d7530589c288064e7b496b7184387d1d4686c18f22110364c55e53f3a70d016832be570218a012f6b252d06f73d254937d9062a293346adc20823419633ffed17822bc3259dc061a1a8863d10da15f11186f71f04b20e0d152385bc108ff740a68bf824f890f05b422bc8f53d3255aff1e028a79f6da0e0e24e931497d03ca96b04e687a6f6300ef2bf5af84353c1e1a3aec79fe45be15ff7c3791aa7dbb919da84d1106515f2d5ecbc9c4928ef292fc4de434d4b92ac996d21ebf2d7d0b9d99a295622cf2ed2ef5cbf883993dd846bae8f4fdbae671e7338c6e1743fb26ecfd0e40e409c60b03445ba5b1bad93b6d8fd88b0dfb82ea5a9a01db23cf5f74ec68b4ea42a309c624208abd552ce7d9b316865cbfad7d0c232c8f3589cc58f93168ad5fef7c3843002e357ed1e5708133f433b4e0976a4b1d8f75d99556678833e17699e399f9970d0053375c14a0ab5cd8135fe265e537ae1d98e32f64258458b7ca404128db1e2c187a91f43be9f07bbdf161b002391f1b31d31a2c8f62d7dab2f8d7c4e618a71a943d0f30fbce765f0a81a0b86f000a9edca80be34d9fb22fd5959a52dff8b214d45dd690880833365f782eaa2bbdba3cc6758d345dc129399b5ad29e5d8b1f25d06a5eade8a0ef42d3759d662b6c3b27d9ca7f5804c2b3e92f1cbf128efb05252410af5cc57c287a8f8c6c3e7e83ac898b59003c3c39da20e238659b09f1df157216dafe77fb079590574e4b4123bf304f18960ac407b2405de4825cc00c35061d04805f2451cfe3e026bb617a8e284ae1bb816526a9c506a33186bdfacbd016102199b0ef23d253849057f884b691e22de105d33dd87777305d5ea3c564ff00af3dfa4db7f4b2aa305bda33a2476a6b05cce69d61d9f75f986dfc4527af3f19102cb5b69b2842d6d7e9e90e8d575adbc16ad8a3003209e911b2a11d4e200225ec90847799d4821848766737493f708ece7a692e4885692d011ceade273fc4041c5d41854376102a27aceaa0aaa4c3257eb59982599d817fb2605623931306009cb304bbce9135174946a02bb039b7995536a8c4dbb218259ddb2c1c5b9dd411f093e4e518d590f06f8c106fa0f4df4b40f10db27b63d53eb4decb2481d15a1b1f9a4dcf66168ae5f6d7c79354ec366e79b5875482aed5d9a686fdb62b17630dc56a8b87bc1dcb6329bf2d19a56110dcc82f939acb1a6e70e994bb94ce7ff31a3b1db0cf696f5c842eabdc0a6e9d5372ecb94426fc8faef668ab3d9576705e304a945cf241ce3038a11c6f5622a3422ca25d6d0040b99e5cc4e4b39843b7d72534843b4222447511a5174d7c860e10896b36b505a0e16e37cdf8ac5691664e1e5ea49b6f6f35384f56d9faa8b6aa800fd2444e741c31814b3bb2c8c6cdf828187c46187808eac8eed732b528bd82ac8f43e6db3dcd19ed12205172f578245a1589aff49acc083e14f1caa589eabd4d490f6940acbabb055c05319dcded45bf21f0d43708de5ca2383859008ca641deed357917f5ec7ba58101d82a5cc8a6f524f19284b599173697a880f26ba158d39dde2cf107c3dec0a9723aa76b6bad28134544a94eff974c94c041ba956834fb7b8a20cb3f9594b54e896704c00828401eb747c909f8e2fd22eab075a9078c84b13b6bbac635ae9abaadb151c6e827b606742f6ff2939ada26729759830191df4708924fea7ebfebcff8f927d22df05724b550de94617b485e6049826fe78c7dcef0a4b2d23fcfd60d744b89dd19180f1b06d00b6b4c79b71bd446267e49ceaf11951b2d9082b09d0e3489fe070a744a0e64fa7d24d7703e2e82a7f26381ca659b1f66255b7efd0df075b6ba4de7a085030738dcb43875953999c2f7d5392df24b864833ccc9bca50062a633f8e7b8150bb74b7a7c537b12c72b109f8c212bdc542940fcbe580040f301baf107ae48ae17da9f15e0d3439be0bd2a45788973bf8745d03428478327de779fb78c055f53138a8e8df3e15405872ddae7b1ce4515871a20b0787503e861c20abb64c517e0042a2680e7a04527301326182626e560ba34cbcd567a6fbb797077ae12ee618312c49cbfef179c6440930137664d6cc87902a61de833c0aed0a616790b8a9ff71c492e8944bc3f60fe2de37788fef01f939351f4b1fc27098590ebadd75746f60b012641c5ef934924a686fb002c329d97277967b2da3706e342c7418340cef72d0392b4fae0d4ac069893efedb6739f3bf6c4ccc98c201a908dd8f2c46ec94720c00cce419319dfc91c3b842b3e8591e284dca480d39268d31117cdd8efc4e520f283358f61913978ac3430c515e7517268d57226dfab2e09755fd0fbb8654723c336ff3932cd6cd57377fc84ad99e65e92e3e62a10cd8cb84d1eabe63874b70602ec92c43565c29787cfbf0eb1066ce59023d4ae6b066217bdc050dfad86e31c4e357566f631a0ec0d3fa61c328756f7e8e9d5b3b7c8f86cbc08daabc2e8f31bf8eb84ff8a4a6db3643f1495b21c73cad93e3e2fd220aaf09a29cf4c9c5864053a1331b441aa1c585625e656e3e58df740cf3d457c40b0429e10e67b7544310643409645badd52389b0023568f354686018d5cc617ca0be53ea64144ab52b5109a28e74e789766193593c8bad70d10b403c06dfd7ca3b97079f9a4974f563a19019c8c895f9372f50226c3b0976ba2dcba703af39b1c50c7a46b720ba7d6491ee9143c7b653930901b4617eac146fee04757443f36c7156af58c8bb33670bf2928276bea49e7849cc27cafdf183902bfbed16820c68c2238e877290b90a118068947b5a8977360e5a952e0036257dee5e0443433bc39b931f3a78a42a124210a51c58cfd80c56b67a52a3238b4a47524f9e269369e91ec03c1b7ec246c04420d0035d930d1a4936dae183715bee51356873764905f7e7ca33c3f31a56996e22caba046602b6b24130d134eccc1cfaa4eeb4a5eba6fa0cbc022057243d8d478035a50d38b9c15b98af87ef36ccfcb1a41265398b117976d06a7b604b0945f6724c34befafe430b6e687635eb05e98b27b9e57fc05ceac658bbf43be2b055ceb12385938b72f3e4e0ec92cbec13b6b816fa60b5404b418c33fe661142a616b6e24e8f9c3bb35d37e457d1dac5b9c74f471beaf7f8f4d26f608f520ccf0e113801460fb5f0d9b7c6ec1f81756e4486d34752010a0dfd19ff23b9bab9cdcfb1df1251615cb508e4922ecfab788decf45b7e877c73e46391f8e7368dbc5f351ebce144bd31203ce49b741425627c097ec248008f704ab6321f4c59e6e6c1adcf21d255b9c0abc5e5d1e913335b2f5c18fed758628396b06073450bdc8e51b9ac17a15988467eb7f3ec60395c779f28a2a1d9998f24515ca5b47eb5cc29f875c9d1a1165b3fa07fd3b99b18269fa72901035818a9bd02031ad308ff76ede118c97350024db3130d1c0b83bc7b65c3bb1f886c3996c4cd8939139aeb8e749c34ff131274570245f1e762ee28f9876c9e240e56dbafeda6fe7047f8f56148b0440f23f0d011a69b14d6dc35825881a3eba8c695d89526348a2e783135364552adcb2de2bd4bb42f48491930f07c20e89c9f87dc463d93e72a7162f518cab3474af3cd60888cd45bad31620195330ad1fa68c7b800b83761b351b440ee1542ebddff7fc14b018c913564c935e83398d0e28e5cbfd160707ca15b6e4efde70def302103524f2d9162a684c86e6dd17b74948e34b7523839b5557457dae8b84d9679010b82b4f9b6ccffc1777904eda4e4583e195476f62b6b2fc96fa400281d935327130216f637efce4bd7967925161f603ef63854cd24c1637de8a8fb2fcc7031bfba6108270c3dcf4e240a99c15115a88522efafbcb2905c304d35cc0db64799797a10e49bd25c35c762064bf13db5ed47baaf7dd7c0f4a4e768926eb2b5f9f2b7e9b0780258805e32665ef178dd88e89fbeb7c2dbdc7f4c7672d6cfa8a46075a304407aba54c51707cf93558bf6f2bc15e138ce94d38ef015c8abdb37dc627159b601389b40547fdd18c07caa2e8c156ac50719ee2e1e57d111bd79ae6d03a1297a40c36bfcb7110608d12d503545f26013b67f6883ade09afc1a9ec2287c5e0b4fc18961675c195b7025239a4f728b6354262a3b333a9f49a39f5f740652ce6fb271e03230bc5559de174f64dd0d90f02b5f2a2eaf004278215a9d856042f48cb3e2028a458428b511f716a00e9432a28ba15a6eb8cfd6a26c33641dcf788c1f6a71cfdd7f6043c8cf84ada544f719aad323ec1d2ee9f5a4888114e008cf4c467cd2b509709b141b7d822abeb2f1122d0735485f29454e0077ef21016ddcd68fbe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba01000000000000000000000000000000008d297f772405c35b14daacbd6b92f436000000000000000000000000000000006134a41a9eb8ba562f5ed7df154f8b7001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "test_exit_code": { - "crisp": 0, - "folded_export": 0, - "enclave_contracts": 0 - } -} diff --git a/circuits/benchmarks/results_insecure/integration_summary.json b/circuits/benchmarks/results_insecure/integration_summary.json deleted file mode 100644 index 68dd92015..000000000 --- a/circuits/benchmarks/results_insecure/integration_summary.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "integration_test": "test_trbfv_actor", - "multithread": { - "rayon_threads": 13, - "max_simultaneous_rayon_tasks": 1, - "cores_available": 14 - }, - "operation_timings": [ - { - "name": "CalculateDecryptionKey", - "avg_seconds": 0.113595944, - "runs": 3, - "total_seconds": 0.340787834 - }, - { - "name": "CalculateDecryptionShare", - "avg_seconds": 0.606440319, - "runs": 3, - "total_seconds": 1.819320959 - }, - { - "name": "CalculateThresholdDecryption", - "avg_seconds": 0.558063459, - "runs": 1, - "total_seconds": 0.558063459 - }, - { - "name": "GenEsiSss", - "avg_seconds": 0.124646278, - "runs": 3, - "total_seconds": 0.373938834 - }, - { - "name": "GenPkShareAndSkSss", - "avg_seconds": 0.227111444, - "runs": 3, - "total_seconds": 0.681334333 - }, - { - "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.433242125, - "runs": 1, - "total_seconds": 8.433242125 - }, - { - "name": "ZkDecryptionAggregation", - "avg_seconds": 49.593938792, - "runs": 1, - "total_seconds": 49.593938792 - }, - { - "name": "ZkDkgAggregation", - "avg_seconds": 20.539657042, - "runs": 1, - "total_seconds": 20.539657042 - }, - { - "name": "ZkDkgShareDecryption", - "avg_seconds": 1.48974143, - "runs": 6, - "total_seconds": 8.938448582 - }, - { - "name": "ZkNodeDkgFold", - "avg_seconds": 62.378365152, - "runs": 3, - "total_seconds": 187.135095458 - }, - { - "name": "ZkPkAggregation", - "avg_seconds": 2.134972792, - "runs": 1, - "total_seconds": 2.134972792 - }, - { - "name": "ZkPkBfv", - "avg_seconds": 0.340530111, - "runs": 3, - "total_seconds": 1.021590333 - }, - { - "name": "ZkPkGeneration", - "avg_seconds": 1.378689777, - "runs": 3, - "total_seconds": 4.136069333 - }, - { - "name": "ZkShareComputation", - "avg_seconds": 2.756735736, - "runs": 6, - "total_seconds": 16.540414418 - }, - { - "name": "ZkShareEncryption", - "avg_seconds": 2.547185821, - "runs": 24, - "total_seconds": 61.132459711 - }, - { - "name": "ZkThresholdShareDecryption", - "avg_seconds": 6.14141, - "runs": 3, - "total_seconds": 18.42423 - }, - { - "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.097622333, - "runs": 3, - "total_seconds": 0.292867001 - }, - { - "name": "ZkVerifyShareProofs", - "avg_seconds": 0.228617342, - "runs": 5, - "total_seconds": 1.14308671 - } - ], - "operation_timings_total_seconds": 383.239517716, - "timings_seconds": [ - { - "label": "Starting trbfv actor test", - "seconds": 0e-9 - }, - { - "label": "Setup completed", - "seconds": 3.049949667 - }, - { - "label": "Committee Setup Completed", - "seconds": 20.248450958 - }, - { - "label": "Committee Finalization Complete", - "seconds": 0.005924208 - }, - { - "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 305.202475458 - }, - { - "label": "E3Request -> PublicKeyAggregated", - "seconds": 307.797721583 - }, - { - "label": "Application CT Gen", - "seconds": 0.3082625 - }, - { - "label": "Running FHE Application", - "seconds": 0.003257541 - }, - { - "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 79.923254834 - }, - { - "label": "Entire Test", - "seconds": 411.343670791 - } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000003f7062d91e599a1d9000000000000000000000000000000000000000000000008cc7102c928c65c450000000000000000000000000000000000000000000000075ba3dc02d69ce9910000000000000000000000000000000000000000000000000000ed71a67536b000000000000000000000000000000000000000000000000e44e8edaa774d8d700000000000000000000000000000000000000000000000069df7c477ac356a5a00000000000000000000000000000000000000000000000546c35bc9a1e69c5500000000000000000000000000000000000000000000000000021ee487cecdf100000000000000000000000000000000000000000000000ab881c361ac7b250800000000000000000000000000000000000000000000000615484820700c856a00000000000000000000000000000000000000000000000bb5ddeeb0299d72f60000000000000000000000000000000000000000000000000000614a8dc34464000000000000000000000000000000000000000000000008f86b47c227146d86000000000000000000000000000000000000000000000005b0124c6d784e70a800000000000000000000000000000000000000000000000c7ce22f6009a0150300000000000000000000000000000000000000000000000000028c352ad51c1f0a1e934d28e26ddbc2239f70f1f2a218f011eacde911ac55ca4f56e4c92826ba0151ec8baa1515407b51df92e0368977054f4ff1d48781d8c1bc7962286ed7d20c520eaffc4f8e6351751bd8fe1e162471d0fe3163d177d0ead3ac22d2519a17089d86113a4a8393c93b80f3bb7c9dd5bab24c560c9f0d8906322ab4d14bdf2527575e8ae48fb40af0adc9c7df54fcb65138a862afbaf04d592215d462bcd29107c4ee919449aaed15296c3db7bc5d246d1753696a3fb0d71ae9e02085318c402afd41a4633e857acd0967c13bf26c50c6457ab5dd898ac1864799b9730689d52ad93c530511536a3714b5748aa649245ab18e286181818ec79f628930d4b39d1af275a9bbc79f11513fc819f8a77429a99f5c19dbc3aaa699028fd84578ae771d2cb42f9080e97577d5433096dc7b5872c8a57058cf2b02f8c0e531169ac86d010bdbafbdca84bfb0e04bad65fe3350d55fbfd17c3806f415f2fef12c85a5cb05693555777afb1ed8c9e98ab2221191471b57f70880883d7f77cf7774d4fa8726d1e7542a8d79306c76a221eaf68d96dcc25f7bfa67547c7e7e6823a8f5036a0520516bf26d60b94a8dc1de281b183f97c760db8ac7dd87c986a7ab87c57a3521148a502af09952b50e41ce4798bccddf61aefc5c08cbe35204f43d0c36954a0e89229a121eb3d18ac0c9561aed66af56e683b1ba590eaa4edc1d6476c2762208096dced400b6f35fcbf000acf60ad54d9076eec61d80121dfff0afe26e749c2c7dc41f5ec8f3774ba1084aa7c4d494798c31c4b33f0371083a13e67aaaa9fb297e0ddb44886687d5d35b7485cb47105303ab9d1a237f8dd9dcb93298077c2c1bf65619bd87d561ea96e41c5106896c2a1307eae3afc8ce2c501e6ae5183d9b302f94e692484cba7c25419a42dac6badcb139955e149eccdee3c1b2bbb39bd92006d24ae9e0b0607f5c1994f5c0eec60fdd95dcd71c256fde7e12934ae95ff62c1c3afe38e4250234c03b3fbe574ebdc4b6acce335cffc79409260039c2c5890f33e7fc5c1904725888d81fbb00dfe5829b22b86c60cc60282639614406840619a2a11d45659615c74b56c03d94902360a16b8813b318f90cfdf6d941bd27c11c2c40020df0c8df1b5f56cf8faf6e3f78bdd34f134ab0857d93cc7fc0d8d26a084aeb7c91a1d79ae529ded891a7a2a94b774927a6747644bc87afa45c115cef199c133466b246d8a8f6a87f50454203689ffe4a63c722e36cd714d94d5d8ec715203e52c6915f66cbd5ef50437c703b5c3393cc645517d6c2a210f9ac23d88a2d480d46aa3febce868435cb4052c6af52d836aa60a7141cd972100fdea65c6623f51577a4fdabe52375af64863d09b294de12c786066a4510cf18737b5cb2521bbd58b29c859db5be07fa8057c61e27972541464105d7346270eb1eaf8183f00b03e6634f391192f98965c1fe150acaf8decb6acd8ae9578aba334c8ceb816f220f0a141a0b9c666e712d8917ceae41edfa816a4a3cc319116df04818fed2c5199163f3eb27e7de5e4d47b7a22a1e7e7186dc3a4ec5744e6b26dc0ac4fe236317e300bfa4ac8d1c52a22a867753dbec2a0ef3ea56b28a978dc21c6d56e0a9822cab56055b0a192a4f2a1fa9a1e2ad0d98fb85051bad9fcefaff281aeb31781b070f5b929a644a885cc948d2b8e9b7948b588a829b836bd61f9087f753533e5c0e79b5fc84e0de7671e00294fc3a78e2ad1e7d469a0e2cbc379f6494954a946d23e35a296d5c94f4cf03f663f7328be01a3aa375f6adfc6e3e10f89456df78a3240903d00116a9b3572d29437bfede50d55590b4df762150fbfdd6fee2c519302c37cdbd0ca0131da0faef83a2b57f89150a76f67a22fb2825ae844a2c0e55a62c00585cf13f7c290242ac5610faa0fee3018541c5733b68415f6f999951b90921e98735f1adb294593b17e6678722213e8c446ad3a7ac022936b20a6d77b0f6176f505aabdbc699bbc6e19d082fb8318459555069abbba3d01c1b9b552da6c8174ffa28093ab52aa34c5a04e72119ca3dbeb427c31876f8a012fa6be8dbd2a81076baa410047ec186a7ca713a00714cf370d06de1847c65f81e0023042b1f491eeeff34d3b6e787870dbef2665bc02dca4ec8c0fddf496de2e31c7450d626fd10ea411776b22e3ceb7f3891f81080eabfbe32548bb88663fa270ebba72fc8c5243643188bc9ee4d07de409f501cbfa49afec7c87d1f2789d2f932f298ccc33c0fb18246887d583e8a5966746fb3bae0cb7f3053e7d807c47ee679166cfac9f11031de5328aa1e8cbbfe748655b5ac3f7b303a92328c64d738e337e2c88e4b1815aa4e0f6573d470282ce50581e75a13f328a3a0b9a1c263f5180223110576da26d8a27a0e4e674729f076e96b629ad9b2501d8f8d437d8ed00e908692403aa12b370df72df67acd5d66fbd6d6e9f3341101c4fe455c6429cbdc3d843b52ae36215a43ac8698db6dcb1ce12497b9f51d8cd0c4b78b96bd81f24d574177a98f2c19090146ab59f7d5f461a0c6bdad1e75b6da38e9147b88a655ca582581ad2b64274c7a63e1cfd278616f7d185902cc57cd18f89bbb4ce2dd17cecebad3c3ae812450131532f01a9c5c62f6bd9dbd817f5b7a07ed301ed9d92083d0724cf8a2221e263a087933e462f1975e9d2aef17772cb92841fe96566553bf90ad051da81e0bb61346c75d7de2a99a99001e56bd9dbb08dab169ec147bd7da07e01518d6951476e1758afce02df80c3fbdc515f04cb5bac48c201aa8b1fe54a520908d01d71def68a4993955aee64e9ca2d4b85bff5404f51696a7f26ca335b112900df31029b365ff086b60ab3d13c2c44465a1656c82a1472c6471bd0a018542139b4bee2741b75a8797dca4cdea4df105561471e1925e46a05a495ccb95a62bda8f2fa905bf990e5f7a30b9ec101e503b1c44ce94d6d751db5d4d01adb185e7e3de9f27268e33e71fc0136c349931194a638570f3771419d2b2dcddd74580e61cd0e1d82651a4f74f8bcb480b02daa45a841fc3f76890eff255b85a88bc204442cc35b501e65b950d5618397f528e8835d8167c888594c0b3d3d56335e4968440b7579c18a71da291ff2dd20bff984b0809d0a72ffa9aa15e477ab40e998e1845754979039b052e98b768bd8ec0df44278c207269026354eefbf6d32494347009c580731b668298f1615b5fd77cf4d43bf2f6319db631859267780752e728fb52d39eaa1f8794c0aabcd93d68844a49b146200a8dae0284da464d105fb70dff102798180231d29917b244496fab251ab80197d5b680b97fc6560a74d4627bc8702da1430dd7c2e80e72a633dc52613da653e24c43173f0374fb9ee7c31d45109a2f2dda1a6ed6e02c387274f3dd41d3d30ebbe2d0e5be93a288321e54eaafee3ae3408d2a209a285a1286870d7081d8d7987d96c0d96ca7d38d53de6db74497ebe57c97239e24d134e668da1e614fc31b92e571228eeb9eeee087b5e87183899701652d1223a0c17880be1879a41e3e4444a426ec0e9fa4af9628907a42e4a40d9aeb57244d40642f454a684f07fb45974600f06e72ba7ae4dc8ebe05a4346ff12279811e1e8ac8dabd36dc3b58fe17eb6bda441f9d75fae5a431f131ef567c49334a2026b1b06d0d09e4dc77fd44f9bba7003e5f6b6b6a78d19cd2ddea68492d8d5cf918fd982caf0cf885d3a0819ffb8da3d24352207baa1802784bde523e1d4071772552c303070de53ae5a62aae59b858aed5260f66cf269d2fbe57df7c3960c84c00ce310e336aadf9d8ac732b8504f61775296afd57ac458c4b3f587e9095473628876de23bdcea9df2fefd44148b794984b966027bb3835f4deb5d0323e3d8512839d9d7e91bbd6d0b579a8269256fadf864f388ffca58afddd505f1009a887403f654353a693b5449c09424816a4f223999c0261b85fc9e27f8044a685fd97e21c348d3131cd0ad8ab11361ad8bd542e13b95cc94bfc6f4e02864e77bc3a5ed27eadddc4e835359c11b295e862d4647e0be8151f1472a2bad617d662049c258270ba05cc3c8fee60b79dbe3cdd5892d467eb1eb919b10908c1e6bce7cd6256f08b90d498edff9b990d45e8517e68b155bde445476e86361a62bfca66c782d9e1d5020f5aac68add0d8d5e11313bd34806eb1d07998bac8aac78045bf62ef35d1e6c68699ae309a357eb91767cc97f41afc839fc0e2acf149f03f5e4768ffe12239d5ec835b63dedd8ef257ae0ecf932ea82089a9b334f50443d89da8b9d940d17cf45a436d206057e1baac60dd3d4fd5053184b5fb725563e2f5dac366e47a41671e27bfa1af5d7604965af6f65f9cbc0e187826e3034396ad39269a7828deb009d86c1876e90e2bdaf2310456e63048c0ad17f98d7e3405a57c62f0891862c22868eac6f10b58ad7476cedf652fcb48c170c458ea95e0dab1300d1477d371a0a83855b2b4be0f913d533698dc00b61e6e0d98172e11cfecd9f2ec88fe7e1282a8e3523974469c524c395b19cd1b2ae2e6a0f08b8b7e9925dd16241984f02fd028654ca0f201085d96833d6c571a08c2b680b77e6374767fd732a315e12b8c0150e7ce2da2707c402b007853edf703e5a06659abccc5c7d539a6303c10a747a1e1e6c660a3493a44ad142c672094981fe9b0699eb6202ee481875332cde412924e4d4f54c75ba290478813ee7b7f8d2cb73c42bdc9b4db044a2c033356a397d2c4c27dacccc0604af0ab1b240b25d79bd8efba3cc58a7d4e98a56ef93e694d61dd9e5f7b64e66744fba85443990e2ed06eec8d4edba99ed139bc3fc816ff1e925b004f64a5e0fad8b437791220910fb7619b648984bb76620b9995c1e0a69a700c51f7b25bbc228f229520d7655200d19e0e155637260069e8ecaadaf56dfda0b606943398c2acd8244ef5e50f797377dfc6fa9ee06f4275f03912c8b039a4e10545dafb7eb5d56f282fddbe00bfa22c42731fd13dcd08237f88c870a6b02d821c0cc4c3cbaee07da622638faeca1c035134da588eb3a47298ff1f76c5bcb1b07713872aefcf57c09befd5274046db1e94bbf346a9618cec87c5d833b3cb6bd1fb1d5fe9d37ad9b8f7d69d27e74ee6d11442b4072971d6e308c90d2f28f692c154ec89bf6cbdc150ce4340973cbcaf071d3a6b86f43c96c95bb68fe435b719e204f06a549b9859201bfb3c8f674334d15b92441bf420dad6b8ba7ed93a4b10d2446d9f477b8f4ec9d7d61dd89ffaf24851b9e1af79ef09d04fe4c7d3b8aa5fc0da2c0013fa968fc23f85734436cd57b203429df6c86e228b25d897c432a70cb14e82c91259ac6b99a472f04cb05da560a649d84b1d96e532adbd39ce1d118e024519d5d192db13745d58bd16158c16f79f890268fe33acf3640cd7fbd97228a0ad8f596ad7c30f5fd9a30c3a58d741a2d1248c01cf56fb12d646351b1d7bdec09b42a222f01fd7c10403cefcf03c0fea4708557da361d24f964088d362babea2032fff91add268b7e2260474cc0c1aae1ec3526be33d9724af9298ae7e94117125ed9034842758be7da9d9f46146f157bcdf61f8f0ce96b32970cf633a17db02a03183311f34d9192fb47068e905383e1fb9a893dbe99e7852c091eb056d2df2a36fcf2ea043a1f9f80df7d2e61ed2d3f3294a2bd6ce92f31dc6d8bbdab4f6d1c9e678355d1fcf76296effeb331d4e2fe67a71da03da01f1a18cf069553eaaa13980e46467f02c69bea54c3720548b3123a35dc4d419d66c373b9089ce74d2f1c887e07f9068b2b4151380e03ab3782c4d3477698d60306b49d96880133708412e7aa8c948838ad2e7c574a87420d2eaf8a25535161adfaf0c5dc8f246e6f831f0b0d07d5c1786ecabd3ba2c3c693d53d95d3c61376e795acc867eeedc7c22e069b5d13c0155fb52b2f949bacdfedadeebf5db8d0bf6c78a11124a650873d00014105f5c4d5e9a76723d3c67939a223b0e21dd47410856f6109731f3fd4573914528d4d0451b6e9852c06874f6a8f499644f82ff8016134533e19e7543937fa1b79e556eb2a677f43c40ec09e8c2385b5e5dda6215ab070253c5d3e53b2ff7a00a2bc71a7012e60f4ac98fe1840e7be8c09aa648b3ab0f0c27085569d6460b8028f028a0fe35cbc839b5c9e28401bda72c3972c2888d203f378d76751e957740c1177fdc8d98ddc08e28b05c069efa51c8252cbe0cf818abfcd395484981c0e2b80cc558a8a10aab8b962daf92a786433511af9f8527aa2a49c2b8bd53101d105fc145055d9ba564709104c5549bb24a3dd91a505e47959f719258a1cc6dccd06c3b905097f428b5cff87707bd0e1ce71ece41f31ad817ff1f3d04630274e7707bac1c17fe327bb81353d07980eb53d4602c580dda7f7f15e59e57cd7dbd3d90613f1fd884030e3bbfcb0d7efdc94158660e2fa5c7d49d11e08a9b723087892209124075b7d82394c4ac6a6a3e6744b706cf7f538fbe24358c280b1c6d241ec04a10e305eb956910f0d968d0f83280049572e3e7206b91ca83f558ff6a01694227174a5061617717a39664d6b54a851e1ab5c9041c80952a220f62d183fd4ff02f9d499f6808334fb92a96a4a1332be8b96b264d7ace5e3dde89aa066da81471f8dc4f4c40b4ff98e4b85557de6e994baccc86846ab05cd2e6ab2f7affcf99c008ccbcbedd42d97aa344146ad1359db6ef1c3c8e7ee17fbbadcf7d53c7b749b00d57fead071621cb4046c8d9c40b138b1e897b30d7872b654bf975575b18b540e2b702474b75054fd96c52543d85e68751471a807905b532f7b72afa62bc86c08d20514704803dc419c284bc0b953f95b2c5c957979546e79b46c2596fcd7bb07804adccc6532e423df312c056d852c6730935f95c1ff85556161503263da9e10b45be2e0cfbcc72420fec0109fef3fc1d69a1610ae256cda6fa0b1edb22e6325599a344ed9500bb3b6db80b3ed4b856d798aeb51be3bacd83e3ece79a68d750423ee12cd58ed06961b157814caebef95e7bb0384213e8f34c3f364f7e0479b248d3ce8d2005755b5832407fc03cfdfd570903d70ab82f55e01499f0e533dff1d1ce6f0c124b0fa6dd880e2c715a08bd9834659fb5dad9de7e79fed5471144c0caea76eb72fd11995bbcd26b93c3eeef7bf4613270088c9d292c5b16a2b9c5f064ed3eb9f013c40d3b53e08486724ba493c06a4d9ec7d768aacb0ae7e9168c0028372c10a7600ef9e464c2617a6185c7f9f1517cf7925c3516a0bf6a8f6e7e229a8fa104e164b8cb328d16eb11f3ea40f968302cd6aeff674f113a0d5f4eec61d57142b4efbbaf733db8415459c129ac5721a1b1bea5a025da135349fef2e86031668421ede6358b63d5078c2bedf744d0b9304603ffa2726d02e8c9bedcddb10e57654bddbf2ff23134fd892ddaff5e13c7571c673578d336aa4cd6f7ca9f30fa7ff2cf4ea874d55be1e755b749a2246ca93b7bd613e24c52ecd0e0190563713f2df9406088e6b2cd92d9b03b4e3affe99b7624321bd6b462fbdf3235bcfe52871605f0cada9534cc6bb07b31b133dea6b0f533aeb70ec48236de198c427080056e3d16a420157d335b08369f69b8897645453e8087753b82fcf8522406be11fbef23cf74e343dd70144cfa5ee7e67ab40a2dc21902315b46772eb4275bb841254da0b779691c5f56122b367f86dff07a34bdbc053a9e56dfc4afd69fa3609198d4cc852a9b2707922e89d1b91ebb02dd602dbe9cdcfc74022e67aa0eac4fd1261d7f338e3f31aa22e69b5d4217900caf7606bac392cb7b46b96b21305ed0b0781389ac7d3e23b955e9358d57eb4735c8ebaad59d1f2349dcc16bdaae8c0081d29f21df52ff42d437137ea915aa96d1e286e1a88de177775f59064fb4a84fe06aa67f3a50812921752d0fc249387a70a15aaf42811dcbbcde646c49f404e8f09121fd977cc344c9126d5a8139cc5afa4c15e555805e2edb6fe4a26460990462fabe10ea395785faa72127618e810bdabf626a3e54f78cda87a4d3c068c09e10663bc532763b0861d40cbe1eb9e8733ffc701df12c7c72a824b1c581aec615a1041c48075e3a7ea820617fc48ae9a815b01b78ca7dacab62c9f4b32028d8e0e0d15d296790ffe2507bfab1653f0e13bc3e70337762110b30e0f2e2c7a86d2ac0bdeb5e0f0ec18899aac0ae393b62e1727b189591efbaaa3853735ea9073d90115f3212a4b7d53472fccd625def7ea36ec894cb964a6dfc9a7ccb369d549c0a125b1f2f6f5fd4fa21b90dbe622eec58efbb1705e26cc444bc5aa0671d1b68d961bb5706653c96d8875eed434702544d6381308c43e8fddb0a5640340a64a287b20ee4d26b56ebf145ea85a4315980f5c1f4e64f97d3ee7269de80f1d5947c1a61fe375768d34e3151255e55d0f6857e895d118541da36e13ebdf6eee9e981dcd0d9b0fb6f9fab694ba9973b04780ae2729596e3c425dc871a404a2bc36bf5a7300e0cc098986f2091345a77f699bf2eb03d648c8f8886e8e103faef891f1f3430d57c6d441288ca100d3cb75b8e3197a571fef30525f4b542350d71170de7b63197434f4adf3c4383e89d67c298c03447e65f8b572898d86bcc8df7d5df4110815a16f3839c72f3ac89b895876947140d4aee3cf1d7ef24a44d41703097a6c912c783b7b2a2d525b86be5b993cee26e637b892bd6233a46d0412808ed53309a126051ffdd0d5e0ba154d1f3580a24c5896d172b523c26bca9f2cb842195ff17803998f55cb444e663539aebb346cb75f5dd2f096ed1989947829c17059d40d3121f726cfef977699f9f5f4c66b9073262f539766dc48a053936113d6cb284ef3182d0a64eadcc863d7267978ca1d1afd73412e3fddd3be739603e57cb3ab538b2daf94efc9e5d3473bee3b714734a85db1367c08b05bad992d964802caf16e360e0e85d8f672e27ade6dd0afcfa6eb56f6181729cbd29af3e38b3b4ecc8a4ed724b723a3ad15890a0520a9cc26af542c1092d15000e9f050f8ace54f722fca9b0fae98c4af61f623dfa3b6547dd7c7f269e4106d91c1fb7362b98c1d36f73195116ba3d41cc2bb48e150c70aeb89257d4830e1c270cb936c127a23dc0c6fc7f62f8d6e4417046d79ade659637b6ffeba9b4cff1906a2088e5f6a9609aa704f9a1165b54e18ce8e325c8671f0a81f9263ecdc7efebbf9b86dd008d5f5f7cee56e1c021e2fe60ad190defec566a5d0bab3da01faadd8362daf771f33955181342d11e1a24c26b5d76a39cc1cf7536241ecb9ac2b798ded0d845d1a27c4630316c321b3545192fe684d5ac406677e9bcb389409a0bb05d0cada8421c5344a8cdb750575e263afadfccb386f60fd58489d0bb4370f58f4dc12936a0c1f7d94e2faa22b29adb0e175cc473c2b2b52586dcd28eeb8a244cc8cb76d5f6fa9ce3842a7f61c3e506b19db42f1e4cd8a4b05faf174375a49a3345d08b0c6544bc814d411b21b8e0930d8cbcd0a0b12d146fc1706a84570251f07d63229e6b54cedfe841b371927bb245fea1098b2d4893abe28bf9cf892f53e29787044c915920868935d1005f86006eccbb4c753a380a3f46ac1467db33834c5acdff2c04978e0457583532f0610d3970d4a9b5b3838447839d1fff0506f28421b675effc4974c3bb02bd22bcd664be35d9e5cdc1ec748617d7ffce93e68f835aaec582277ee1beabf7abe2c63fc73c84ea149425c4ae255b6ef597aa1daeb19f6d5a9aec78ca0264ed51417192e91d71dd8c4b1687cff223ea19889910882de0c4f273b29045dade333512c37500afac03dadf9007c95e3ebe1eb027df638d698a535a9ad67172499858e2032af8d2c6452d9b4a0f925e285a6d03775ad1ef89ec446adfb664f078fac8222af34ebfef8c8e5c5d07332510d9be767a2a8fd3f63fc719180097b492a444a1a6f28902cbbdd05dbf2624db029395faade8c1648f360c5bcf6f00969509f4e2fd5532d79e87172cce7840ae5b302068c4e799bff095a94ee7ae5461e52bcd22078a08923c52e18c127deb635785ef81c67e7244e64c1c377396736dffed3ec1692cf5b8c4f2ea3fbe22fe38682a641486f2c458d951ea9d5fc55680cedd00803fadd8ff3aa101e11c264bdfa027ca68801f1f4c873b99f1f3b4a40abc376a800d97f88e29c845d8d9046509c88f955f9ca06da4815e00533890681dc0fe6733020b784d02b21d43155fdd5073bbcaf3e1566bcc390a9ba9b063062a48a9f3f2a5d3bf4bf23bee163709fde91c96de0c5a68fe89f2efdc3ac38fcb7c1db40c61a51a42e9804b0b1dd73cc84648a2281fb4fef0cac1feb14770e9b801ee189241e0fad6797d640cd9cf2aa1f55f4907794b6a3e4090a0074f1649e835d0f72352f14bb17efa94ebba89f2cc195d19c0a4482ec507bdd852b55c79e9d8c59b6bf0ada7730dae0434db913ece0a06828895b07f39a3e6ede7e9b2c0e1466188e1313c67ac6c4f1f57e5f5557f09fc8aa8d80995ef0505ab76c174d775f52d340d313f9b536af11d44cf8cab2f740329ac458c884671dcaadc9f91629eb57fa5e220f89ead7fa2e99f3aec56bd785433322b26e444bbf3b9ac16d3fbdbeb5caf9881cfe4342d8f74ddfc2b008e5e7f36a2441b8f69eee1c4b9b5920e6cbc1adf92f2d06bee425899d3e1103d87dcb7d890b87cab8fd75dba3d58da32f6b43f6b72a1c68b90a5619cc74439f23f6bfeda6e7a8bdcea0174649192ac8a3077f36e7212f0ef21644f05fbb965313c6b59f1a3f0e5c1eb66a2e4d73fe198551733e78562bb24ecff59b56bdfd4c04ca5ffb49bcf1d8eb8f47561c76454dd44b47618b57011a491c2c7b0c8f582b70a6dbd8e973244bfb585aeef41986969bc766b04dad2b9029f14ccddffdfc94c9c51aec7d3098e666ae5a374071a1d0ebd51e1a2b0a105b7ab3c6ad17aea375f660802b5fa962f9d7c641ade8f21d8419cfe672ca5e2881b2d7d1561136e96ca186e48709f2a388aacabe3793580b722c8d112a0bfd1d0c0af75def97d5d4c73a5cc62be153242459dfb1acf02bdebf66775fb3c52326917db73597a5134cfe5c0aff32db8eb8e8800ff2a98b25d2693445b93b94201097455751c08f78b7f961ab6bb7b72286b293c77d219585c66f12c49921c0e90e4878e1ff88252d4728c52613de28c758fa1fbd8fe1c563055120ac8565f85f0312a3c57ce1e84512a7e6f01d38bb3649eea2b549a89f4c7c9e8d048c6cc5d62fc8399a0a6f85477d1a11355dfac6630d01abdf9ae7612169430e0fa9d77c2118752bb4abc4768d0e78c8c1b258206011ff5114d300e5f9cfad20895f4ecc9a0625adf0a0ed53181adee44f47cdd416e49d035feff88a30d11e73e404098a0b2825d765e0c53f5ed8b6b0050ab489e5524616bd7067728ea8ef25bf42ac3f020e4f9b1e6deaf12a3336ece22c90458e376cc588ec99ad2985536bd510ca9f600f856a8db2170bf53339701ca57eeb9ba5bbbfcb45d3345eac67b15a9a7d845716df6841e09c71346046a67fcb5b51be9129c2d0faea4eea5f7af7fcacdddc530aef1e9d1981bd9a78af2a0d502bddd3748095d619210cf3aa8705c8492e156311474629244272eba57adaa7e846c3fd8fbbaac71b25bc3feccfa94e205e8fdb02e93809f7de44c8910070ecb3ab1dc6e088dfd54655f84063e4ad7caa07870e257f213cb9ba4e34e721d51d8bbec887896bb40502d0f4646bda6254059093df14e661cdf5d9fa1a86e2f31eeaedae100990227762b9f2d8ec60afecd697a2d02268e342042b0d69d47305455a40599e01a483736fc1d9becf554c695b07a27a27bf096c3282aad2d900c6b7a8fad55643307493f8f5bdad4c3f8d723a97c8a701fb4e51bc063eb1d8bc47ce98ad16ce5b5ffd15b5f7074ed14e9861552e994912401846c12d1ff7905485680f6a0a49cbf4e970e5e141863a92f64c2bfbfd1a1174f603d2a3e484cffbec5010c3525dc2c6c8defe0c84bf8fe3cfeaf297ea30291d98fa5c334b60f5b157e576a25f1bc5ffadd50da6da10933815c8c19605ed2cb08b7a1e4fa23741de312de8b96e8dc72c45770535a0225522978f46cf1ed21ab5f36e2809661362bd7d96bf9a687bc852ee38c59529e06742092694ee965306f7f63faed26003a1b3a0722a267a68dd9d8336bbb86153a483b6be5e6d09c82d52f1ca592a4fcfc91aa516c7b437d24fc0cc47ddd3c783900840ffb419c37d28cfc87e9bf208439bcba9dc1b3d8f3a8742bd2d40dfff4c47a402f93cb70ee62ac421452d7f50b41ecedadf39bfb01afac4290ccdb2c506570a3bb837c133ee2b7bd5cdd4f38fa55aa486fffa202bcf0bfa43707f66d8d6dce1da6e9f42f0440e81894e21855aef5b8a1099064194de5d0497407309360e77ab62298cc6a7a7171617950855fa6e0cc6d3b58d131d266a956d1487a5e73347d133f3edcd09f0298a0c490d03d5fdf00ba83fe139d3b3c0eeefa89e3be6978224b59a8a76751d077d4aadc1d2c3b7b8b03ba7c0d41e798665b99587a1349585699f793e882c17151d330f5a5bff59500819e8e6940a17986979f5806dd2f482ab65d5535c42510937a1e876afbf69f5a829709008a489b9424cc64a5056a6aa6940a88b8bfb1b0022da31d46a05fa6e2fe2f0ca37b71ef3294c374da7f869f5c5195a2047f2f52c1606a44856951fc2029a1c1144932a1a8347c3178f7353972d05d6dc04937816a787ee7a67834a972e8c73651955d9c2820d0941b2b9703b7517ab89cddbca15c281d195c64d5968e0b04f50d10f46714a669d7b5b44a498bd3444c78678d219ceda4cea398aa096457e1a8c148cf86c655ca7e1701d895f3a6c35b9e43c95305b3ef3d50f8b2ccb50a77f64ccad9105908ebb80a1b8a2f6a4cd61fe25c453133f46da139b495892c89059549bfbf6ebf55afb5a56936f9e0c4deebeabe56f1cef15bd45d2c8c4abde84c0c5335bf56f1f298106b3b43a002acf52f1bd330825014e510e11718c2925f353bcd7f7e8b2b09b57e4e14ff26b88b6fd0a94dbf308f688b7fe963cbb6669ce06450fba3bcacfe8922a30dfb5c4c43ed85062969f155d0fcf9eb57301a20fd246a091ddd91e58f93c527184325ebf94c262fa2110053485c49ae2de63239543f5660d218f531efaeb5619bacaf6ec1a531a6f3e4d2956f3fba0a2564c97acac533b19e50c7f1ee04e0c99171bb2dd2e62c3ff384f10b21e392f556dec389c7004e57f81976a8638172ccef2a2579de9a2dfefcb670f06a188216fd3507683b4225cabb350fb4debdd0955fddf0af3edb592f89d5321f89e21848a664a152a2f10cd30e5c3062a73f01fac6d93b18df54a98507d7b24e1a17e79d5cd7d7a04b6cf687f64a54f769ee6c3e63ea939943ba3ad71ba7204d2d142645247e71ba82abd6d8417e3e2af10c1744f09afbdb83348b8df388005fa197a28bfb1f8888fa91ac2f37e7c50740639428decaffd29f3d04c45736f22ba59f6cc8c9b9312bdaaf2ad0eca04c6a8ba9e666243cefb2349cc162922e90e82555416ed28ccdf080cd47acdab87e3d04fe234f5b61e56a14cfea2ac4d82142458d38c7226bb448488b603508251114422bdf608ade285070d3fb8fa19be1efadb5e98dba8fe4a0c9bdec550f90ab2bb34ed5fe5ad599d3edf27d5a35dae2aebfb3d296b07cb8f99c4a1c206b0b6b59271893eb9683fe948e6d393664831038ade85996477320edd744404e4bb9f874a5c13b49d2f690a5decca2551ace314736f8cc59be9a8275245c14730b58bfb696f7b903f798b87a17aadff25ca8217d71f6dc06f7292dc614651a3a12e26d678f12bb20f594e36d8bfd1eb61e6562221acc3c70b28ef652043eaeac624d8d1680fba70ef5f8d6e90125087ac8520075b5dec10f13ecbe75a01ced0777297615fba720f5bdf4acf77fbb61fdf6f85281b7cb7e98388ffccb8455c128f6c741d1371e4eb77ff31254000c5c7b986ec23cfda8e2c9d14b82ce9c8786199b8fd5803c96bca8cda0c50d51a31f971ad900d6fc92e57b5c54cb1628a4ad1caa3a5d00dbe25028a0dbd729902088213881229d5a5053a0191ca849866e654f9bd5ba96aca03f1f4124ce3d62b30f4e7998627f297d970ac0b1b53d1d6f6cb845e9c5ad21900150d743eeda160d5ffaed2ff2d85c43efe8d4a31c58ab21732c303232c3878801a379941be034b86223be51713a14bc8f59b8f103095c109e1ffd7adfc4e6dd8f8b6a6aaef1533c482e984b3267ab716b1185ff128ceb78a0d6c98cab05593591c276d0a646c2e4e0d1df6232b8e4a6ac844c7e6eedd02cf16525b9cf067ab4297d849911d9fb588bda630212f042499036156d5eca74d67ff132476867da3277ae180ae38530a23e2ec291a03046c5c50343b4168477e8b03995fd4b52921f85905314d69292b0dd5bacf3f1dd8a3d3211d6021a975cbe496fbbb92db2d214ccd233f12af8bfaf2593c5475177e16836f7ef420d8dde0217b20aaa70b79fa8a173f7ec8cffc3f3d741c6ec11a097061ca3fb6798ec89e5465d56f9f6c56a0f34884f5fdbf17bb467fc428482c033080d26b788806d4be674826f6cb45272eecd195719cbfd29456602e931404d8e783245ccaf9ffbc85e89bcb0a960ef799adfd5306f5a11ff98d386c580b2104a6cb3d665f49103cd770f2953ba77ab813c925e1d3d1befa01320f989200", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" - }, - "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000088faa138749b3265e00000000000000000000000000000000000000000000000ab39d7744ee99e93800000000000000000000000000000000000000000000000b58e3fd596adfb3f900000000000000000000000000000000000000000000000000021543697d80ac00000000000000000000000000000000000000000000000303b482f3002f24d800000000000000000000000000000000000000000000000b85b3e53031c19580000000000000000000000000000000000000000000000003e4b73366064f452100000000000000000000000000000000000000000000000000005aceab8040e500000000000000000000000000000000000000000000000f5dc05a7bfc973e8e00000000000000000000000000000000000000000000000a5d05f4eb90bb6a3a0000000000000000000000000000000000000000000000060872b59764066d290000000000000000000000000000000000000000000000000001421a9133d75e00000000000000000000000000000000000000000000000f4780863c86fff52f0000000000000000000000000000000000000000000000023927881880d93150000000000000000000000000000000000000000000000000dbbcf61dcefb015b0000000000000000000000000000000000000000000000000000d0ae97d8719c1b55ed60d28990fd534c598c83f2d728a5ecfb56bd465a081d6618cec0292cbd237512e8cbd2be838836717f3a7d3b973f5b990c5a7f1ec421ace3f47a651c5d00fb5ff8417a44f464db9e3db570a7873bd28a5dc9a6bd7a85067e4836b68ce825c611f8f6ba443658991339a5094e904964bb9860defcc05dc2482f953fb6061c687a6373bdb5019e78346a3d8b4aa6358b4b56c32b61afb53bafbf7c51ff7415b3a062e79c6977d6d8564fc067ebddac69d8e8a80bc352294d758b31d13e6c168803f0edf6007b840fb864938af01a0d54ed66c99dd832480f57cbf93354d92dbe31fd406fba859e22d0b8c21ee8ad4357f8fcf75b97fad9e3616fce00d82425ab8ea7cc24b56a26eff6f6ca0f8f37eed160f45b06fb8a36fce6ad6d74bba4190b1a98f0cf95221281d936efbdbf97e1b85c57587a3c202109bd02b0ccac1c0865a52812ff25c6ced7a77eb02dc8b801760bceb81b5dd81497fa55c232c7492bc6317bcff50690bc531833b8f056eb38a2cd70a6593954e78d9f2a1cdfa67b0b906a32ff83bd1e5d632f37b869871b08381ce92e74ca749c5fddb7ae40ac0a0f51a01d2b5f7a67480c57b125fa84f186fe9f640b7461b9a0e35abb212f49cd209f27b1113fe34b0798d1c95cd6e28ebae33697debef7f295d707aefb198d9a0bc0e4b323afe706a0149918f17bc9ffefcb1523a0debc9a18cda5beba59ceca0ef0267a1b20842c607949f2257f3d70b75f00faf629ddcbdf935d0d0eda232b01ce76a7013707c66fbf745546f19d041fea89ec58d214f41dde90912477de87296d3bc7695f2a1b16e258c9525cabca394907434b9d2541d5b4ba692d0bb486213c2e5d7a4361343f0b069971fa22f76f36817ce137b60f0b5b4f0d1be40ce60e54f03a0b00effca458b4623092ca8a33bf27a0b8e9772316d687261523011c1e8187225b6ff80a03bfb97eabf1a873051ed965f503149a705b147d3007de670c317fcd6a7a516bd50c772891e2bb3315f6c2a87c3cb1119cddc826f543abc71957b0eac5663718ae339d51af5256268d9a0474a403a6ba3fc81d52d89f71bd2f831f8944aa85d6d3677e3cc4876aac91778000ccf21aa14ba977af5a4d10da02981669450d55161ca8f053a871d81b5106abdd77663f1ef78bbb0eea21e2a20aaa868967a5907ad5582638c573682a851040e436f2e6c9733ee4896a86784b2ca0f330604ba7a3c5b0b746ee42632eb4cf982d4f3c36ec1bdd34c8bdba023b0d20997d570bb040aab67abf15b560b7c04c52513aee47c6646503836ed35d6f16388eeeeeed374906d0dab5fd1dbf8e291cbec2a6927e9bc4c052c66a9870730b20ff69314b4f45b36eae7896c08e5a06e691c8449e48dedf97ddcd3d674f0b0887ebb0c7f788fa577d949c4de8991658cb0f8dc1af2af7f58aba794610ac002f87749f9d104ae27ff60d06695572ea506326c7e6bee213de54288e8bcae89c0e5751fde4c6228122ad8ff186fdf8bb495f2afbeb85a95ee5cec68db708475d117aca0d4cc27198e0fd43ab5d1f9d7ab9de2f1a36e9bf112589daff912cdc442ee6e8aec7409a15a5fe45e5c201e3a052f12e53d1a0e1ce8ad6607c91f8e9c503c1f9c286c2dde7f8d41abc15a6ebe3825163ede6981ccd6fc9b03112ec426a1fbf2b27f200f4adddcbf200880be7f5a73d27cd24d09e4c6e4d111fac582576079d882aecc30f178bcaa94f8eb567064c9ec9ecfa99034620d0406025400b7a25dc35566bb590e2b378dbaaaa71fcb2279e5b11c15b1a9c8b286b0d1e7d3d611a84b62c97b13a07825b337b9b57aaeef4f86253f4deb5674dcc7a909b0ba5a62bf59aa6a4c18e6a975776383169d14695d6b9a3d305e9a21e745b2b2839515c0daabcad6837b062b7d01dc1ba60505955ecb38d334a523d9b1dbb7ebcc565300e8ad930df265a9e0f95e724b5f713967a976ad3ff3430d23786c016722106840bc0c54b8ab48d4f17c802901bd97dceffa70035d35e38d5ff183830fe90caae070aad78800dfd19c788ef024bd49c9ea8160ac550d117d074afad668a4f6bd510ef8a176328fd8d356b31ca7ba711e3afe130b93dde15d246a37c1270b6e92202ddd3604ceeb469bf93dde8652ce468c184857e67d38139cd669a089aa6861e065c283d1803de3005a23494532da0146ed64b85e40b2de3b63ce5844b29b1d404103d4242b9372da3ab04383a671bf91156329c8325f09bca871eb9b2525bde1e641d847edfcd96cc86cdf5b50944b2faf2e9117ed17de6b5ddc68fcffd88c81a07df6735796c3d20ffd3a78ec7882bee8143ef052475edef9bed6517e3b52428923971ec747cf4f5b9b176d23a4ed0119f7a43044e08f766d367b5cd9ef5610ab73ad97dc6bf4d90f55efa1219facdcc830df23431f5629b286602d4ae5ef903199317dd1e0549502cb2eae16f20916a284e792e268e3b7a57b556ea3a42e9267e8565bbaf76819760ea487f2b6442945c0343be29ae94406f3bfc0df8fddc08ac82ba23e9335dc9f9747a73ad89f80083c64e4e0ca3c3ec5bac16c5c4b98829528096d11677e5b2add86d8d3cb2ab4820a27e6b200c99cb89696853ef6f65194da3ed8a69e1e02e7c956f04f25d64f6d43196af74edbcb57c753c75bad26b046355828fe4b56d8e7b094de4c5284acfb686fca6769822013917b3bf609de92f0cd0835181c4e1e9c37cde2a31e9b44c31b5b6404a6c21cb03c5e2ba6abe4e240fb2bfd29aee712736279e486f8a437aff7e469e85bd5897caad3b28a75e0f2577163a579747d38a674aee66657777f9eae6deb89fa31e82ff1f2694aa389b0d32280e5e10ca4652b0592d2f88f3f966aff1518c47d4f7c9de34cd5193199900117b78694a8c54fce6ee189a22d323e097b90b66f4e2f8012977f5ca861f02178255d84c899b963c3307fb1e41bf1f9200de68ac04bd3042eef9907817e6771daf9d3a34117c81a9c27021cfc836938d33259c9d272ad7d1e7a273b7b54c0a152b720fa2026885f1f12bf2656c147cd9c54fe4b76f6cda531400c8178ca9f2013c91ebee44600dfa4fafb44468f6b425e50c634a024094f8cca64359515839300d925b49ff62a8510bdfed6de3f229654068ce3d956f0f4773106f38d8d40908648c87ccd1c64a2d1e621164c253ff9472ccd8c804a718b313952b120e2488267507664201bc55514d572a0c99e79068801972bc67b062ffaf6db639d590000bc5b218af736380dfb85df08d376733e5f09f473bb3cf82902a0b1070756502076279e5874329df3255ecafb485a0df06e08147fe1f3f85ba6070df13f7552e109ce9a119487805b843c229b96ec320193763785454fef9c984ead471d8a0400a44f3eff7fced4690b673718372c28a8b6d6924d2d0ffcb7cd85d77ae1d7e542da333e9ebc4a6d3d8e4a18172c4a282c170024b8be7a889a57fcc75c1486d7d1b942c027af9c7f98beec95bae62e76068b236387c0f10d19c4afee1274960cc29b78f66787e9624cc182ffe8553674830347e79f7beb68a8b33debca0791f242dd0ad1bdea9a27eb052b4a873a88e3bd0f3be38d6447938527ae035114eec551dda82ab01c2c8a87c78f8441448d17832161b30824d95a71b06fd78b051c04f1be9ad79e03364982723397fc744222f42a3f9e50a53f14d94cfe4decd554040033d5be4a7665044745238a59ac3bf71264854501f0ef4ee5f670690d814881c259967a6df24dc395e0ec1c7b9df807e2280a57705836b38ddc025f3ce8ce6b22c59fbff66af05881bada0c7b1db9a5ba9b601b487b49a9fa48678e51b413b471c1e72a1591e1277aeddbc21c9e887e8c96997880d93b2865a610c04f470dd771e8dd57f4d99b31007bde7601a1873837f2a88c0dd1b886c1ed9b7b411393e1800753e85451a0a3669c9f9a909035e6455bcf5d67a1bdfc4aa243a17c16d6ee6100c493ceea0130062e564d26ad2584491a0a7e215674f3ce0aa5b68f3e3f80f02fc04703bd2c4ef3f812bf23717525e2dca862c640765dd9cbb0a897166ede12c52f69ad514c2b963166d8a0c16223a50170f085c3cb1213897891a2a4d0d6f19ba1a0b8e7faded21a7be15188a512a1e6dd25c9bcc6e6a31ef31cad5c0cb8e1932d820675400dd30942d518df2005ae322f8f3190392739e0b2827b725ec9c2d6738d204989cd547ba1cb3bd37ababc597f124283ddc47a82fe8a4fa89c181082cbc4ac86b67bf74625314994d870426a846c6024026b841efb1b1758390e713f1ca562c70d4385dc5f424727419fbfa3c6632ce96cddef10b998450a8c6f601c4418da4d9e1e56c4eb86c8fff01aec4955f1ddd3fad61f249b6e1a74e0520226c7f49df1282612cfbaf389d6f9081269abad88c9915f8329c6f7f10cd30fe22326f40b9c864dff34ed6c32d8934e1d7a4f42a2cfefb8680b1ec3ce5c91aa90004db4643e5d9b5333344b28816904858056b89e854b880fe2ab9c421bbe47d2c8583c3567a93087fb8a72181db348cfc076505940124d9c48723976a84af7a24568430053eee947b3d1d420d1d3f63ff8e9d42e558ced36e2e5d4c70c47d9b28528cb14379044613b775464e86f94f62e29058c9869322648535ee9bcf8f0b12b1ea3acc204f519f48096e19da14a408266e0c75a2f25a47aff402f8205a562df0af8e6b8ccedb6f16128fb2d577d884101b679c954fa42f4ac6715205efac01ae7bd1f024c6132107293a1553365471c18c03ed9bbb3efaba7a9cdcf4afd7116da94d4d486381dae6a51c4554f36a25179d759f998fe4827fca97fdafcccb17f7c3b9b29db30c16573e4df5197879a1251d3a20015accb4d22271892b67442848049adca18dcbabff5331e409d702b9f4ce5dc8fc6efd52d0205a4516d9b40c64c134bcc23d7627d66edf1d4cd52ae985c7acc213a6686aa7fb98a4cdf9cf2d51b9e05b1ddd31dfc6697cc75ccd917f08a92c8ca8c5faf475f0a188ede9370adb00419489e89e97aba84aa6c77d951f624f9aa99955d56625553c3e7a14ac2f1a9afa3f34f815507ead2a646c9dd5a23fb9fad905ed087cd01ecafe4c903c22aff103f2a3ca614f98f8542b65547bf55a39abd637ebde2b6e437a98f54fb517ce908abac19a16771f8e4ba0887e1b3597b168235be0d6c60d3317f9f792e60863b4060fca1d569252f592e4be383d4a6e86310499886aba36263aaa661f701666c210f6b18ba1d8923a713b639129aa452867e0a8cdc1ae0018714ac7e5890ce6651f7545b2d02d5786a59352cf669926d36a6e3f754a84edfd343d7f88dd270c86f6a7c997e90366731500e3fb282d8c87e1c14443fe2ec4443c7efcc51f1a7f9bf1ffa1be301c4fc73d6bbf6a01b5340fa3d004fde4da36228cc8018b8e27eacdb17bbdd7264a4afc92d63c6bfe9efde14b06ea85a69d6b3100ad8021fe0775f8e3026bdc4607fb045cb447f79eac7d124c3c42a094f9f606e54b2d35f52f1c9001bb4bd8ababc8b16501d01ad354ecc264cab305e988705243fb7966552bbdaf95b7047e6266edc6b12e49e88ef2ff364122afeae596f9ce91bd35073214211c436997a4a33d6fef5e70337e672d8381a93f5b4ea1d06c51c21cf2096e1c41634d00c90b794087fb215c836e4a64055cbb9d50de11ecfd6a1c3060663b2b2e216d082dce2579c3994ead2afe49907335ddc49d4fa813729246dce975da1681aa5fde9249e2c1c140de81649f13dbb6e19ae3a9276ea9d0a08e1750309b0b5fb997116c1e20f25234b03c264b71c4a071bbe93b6d0a69e96b3b8edcea6018758a2c7c350a89e5428f07ef709b5bbd06df80ae113dbde9e61c9dbc99f9022595848e4f0adc98fb3c5d9e954a6ebb28eea6929974874e43277ed5837eac650cb979f976b878063d0221e6cb4822bbe42daedf1076495c945aa7144ab839c9010f5803e219631b6d3ec25d9b21c36d1c2a68efb4d8988bc82e44666690f1e8141f3fe11884f2331e475ba32ab2828b967bb83b575028e4d557953ee3b31ddc20cfccfd83d09af4f819ec755b026d64700d8fbff93d76356cf1aceaca758b3c0938ec673153913cd1287aa0ddb0f8c55cebcb8b6bd95e693e32697fd45f0d7f1d346b773dbfa16ba8917577a6db93867c5dfe647fc231a750f07426e89c4f3d200eaf6e0111401ed6742c6aad55fa225545fd321c94f5edc7feb241fe50fae82fb8cc5f9868d4c5c036e2cda9da4e93057ad89a8b90102b0c9ea0ac2eb9d5501113836c665cca9810f9dca35f399ab192dcbb0e7519c8e948014873cf2ca61705d8ff4fe49045287dbabf5332675a57e14ba2810f3e537644190b9fe8dd2d3120d5b7f4fa873c1b794cf3eb623d39e3a81d8f20115bc06dc33bb3eb7e353f242815891e9dcc9c791ffc59e065f8644e2de3f017d563a9b890905bebe871ff65059ecbf4d0d89724072e1f589411113ccc8d23ca44a1fb4025fc2e668a46b8ee14499b147e6396c868861d8005d999377cbda38057848aff4f8cfc8e8dede9dc28fb01a73e4dfde3e362611ddaec86726e945e330c5c20a151ea891fe74467b60d80010d0eb3028bfeee573db93dc2df313c89c1e545cee7af941eda5eb8d76b0d7aa7561c776b8398ea77ea3611f65571284075419c9775b05ae1c25cc9c5270c3f7757b225fbd1bbe5a0d8a96f0a5712ddbf0e5b143e1d4792317599f42bad0316fd73d3a81d365df0237c745529a2dcedc0d7b8f02501807d36936d8a54d42f1757d6ddfd411af39155b234e2ca522cd89a0759d0c9b3009c21e43401e27e22b3bcdfac5a4bf8ee81b1a1089eb4a643126ba01e0a5ee69879e50293f1a85317111cc292cbbaec3354d0863b0707b0cf8da5ca077515b36f5f38e87e4fd70f0c4ab1f4726a7cbe45ef9f9c657aabba65cf75f300f8b159ba9445537e997d1c0a38797ccf19233d14cc678b564aaa62ba7d6fe01172087176fad6c022a03b790b771c9fdabe10966cab2c1f84d6306d5cf48bb0347d80648abb1cbe78018d9c23d23b20d882438917282c6f58e00ad0bdeeddcaa14e7d16afb4a43c54369156120df55e8d76925bc74e69051b0921dc7b0d875c31f39c25c4810020ee8e84c415772d10884a64cd58b1124838dc77ea531ff6a4713ff7df0a33ca14a524a1f526e18a39af4a43224fc7a5963aa752dfa3bc5404e3618c253c596d477babc8da2ba0faa41b3133f67892399ee860d6d5e8d9ee061e39abade8d637732d85f21517083241064b003dbc1841770c5d0bcd04fb2503548694c9d97633514ea2edf6105ca39c7d4b0f3d36fd793044be89b2fa706d58efa0bcb573cf9abe4d11f83f0c183013b5811ae5062ca3bc440b364998342c465f1668d4bbfaf12aa6f410392980d6dd86acf51dea00cba982be6df81d2b8b4cdadd98c3bfb9a91a771744250e604c7d24c359ab15538ae8245cda8ca13e343beb9a85573cd96828039985b30d246e7a5d651c10d38d4347fd971cd456cbc9e78092ebafe7df820a530f3a5c287b615013d1c779c150c58c5e31c09fac48cc02f8efab3734f8a832af325784074698dd42f5b194bd08879c34a6df579956489206b6164519d972288334d47611ea33bb49bc107f82c2345721e796f87f53d326e1ac09c4f18fcab1713ab76d0c7144690b06cb2dc939f5211d1d37a8ceca6f6525adf710fe16c845a4fc7257281b842c19810912bbb1181a95664dcf9cdba87933d18a5048fcfae7664ac7931fb028726b00de1f6167b66edf4b3f8c745d12ab05fdaba8ca21ca7af9da2ec00659d049dde97aa49977a67c603f4c2bab7438f0bae36297c1d9aa9e896ccb361a60e0636664c2e48de1a528a045b3a01560b636d654a39e0dbe256b47723048261f4a59ce977e1e501672a0cec140c227e4aa4ede24653dcc45c5e388676a36072c7fb30172fe6f20ba8947997fac2c3696b3ba00a0d5a72ffcd5303b757baa093f434459a7ba88638f93c652f21efecf51ac4bd77fc06754036ced23b37fed22cef64f7cd5bfd1e0dce9c8a5da6d2974277eeb9c58a9dc3b3c6377f306114421d1197fc743dc2702a514ef2005ed4b34827ca67956924d3dcc86b7d9b13d340a2d78116c10b1bf75ddd470444bb558272220216fff391bf80e073927da0f0528c0154b1ead201809b2fbb9e90d21130e0de106672c7ac9b9258ee31b0ed20e1d2a3ad2f14524dc6dbd6519ad7669c43718baeb840b4df0d60b63ef29e6e9dd2a2592a2485b3d08d02c3c8404e44934216e34044a22759b8b3728f223218b5f295780f9cade7602d5ee2f0e082b8496b6ce04ba731f12b75cd0bdf676cf2af817639ccc9cd465e586fafb46aaa741e92e1261e6b717887d3c59b46614c6755012e169cc4747a8ad9b7362db7c3b79961d0866bfbf14d6d6183088e414c4b3f81761d6c503bb919c930b6fd5183919068902f359610f08722729e69e6688f3710f9782a1fcb7c9761bcabdd9797daab86a468f69163c49e59d931f3325cf2313194ef9805a4822a4426e999d895f9be09611877935c4de99732a2e1b995a04bf293c16f50dbe83edf3b65eb4f1ef97f388090f68a459cc0418b59f4aaade9b54115cd4c9cf9c7cb92fe3eaa5773905f71f9d1be656454b9291abb50c854777260b0765017ff0a27eb26860ece0b01d7c55ba4a8c228a82c9c5ca23e13926c5cc1a833344934d57ffd077d9071e17feea84956531aec7e05bb1dab9756ae3e54b2cac7af90c75f6375648c2f8b674bbd9b185e60de61a8a394b2bba945c779e890ca1867869363f0464d34287fa19894f703d397a8d6a458aeca9d7b4e58183d80a8b15dbb1a653c8682eb2f4b5b15a2b902e4a5dd9a7dd0781e66f354b81a10c2c6afc3f1ee3f06e4cd03706ec2acba94d3cb3bf6f922902a175e7e66c7e72d02e8826662e5e9db072f79431dd1512a7a736fa613a54bc5d0f48301bbe05138a07f996452e14dbba87e625c5c707453408d57a90b5e1c00b32d529124643903a1d630414afe6cbb4844f94564047eb9aa90e20e6e6e8e613dde4522a110711ef246c165a05721424b72ad332073bea9cd9fecd0c17399707dcafe6ed9bdc294a037e9338dc66c09b9bbe36985c5b021b32332dd917851e19901e49fe96834d851f697162287487b87c4551915ab4b151622c982acd7b0b9929d96c94229bb39b294f5b3cfb484e00e734c1f1cf554245e8ed206230380af73e79c4db31bfac732de6f61ef04cb748a47dbf487ed550657ff86e49bee29a65b1715a2a7e5d3ab32e1b1433ad80a8dea89f7575572699057956128d7e99718f00c6fa760e017f0d2ab1d11445f18995ddee563f4be7edbc55c00ff911054d40b1a55aadac71d64609546a0bf7e60d23278f4f271af9f104608d828ddf9eb57be964fc18f864767121d36926304da1c1a148a52ed35f5e1a3f6a51829d2e9229d46397235df4a5040cc43d240a26cc55434b407257e196ada5d493a715143f5cdea8530e1d45d1e92c391930f76ad6d19ec4ffe89a0bc977aa9ab20c15ecc1f20258e01a98876cf12347696e548b31dee516049b98cde66dfe19251ba1ff94020dabe324b200f0a7050a238845983e210c9e15e8f3ec7bf2e6f18725e4473eb3dbe7c9d3672183260b6a78bfc93809d22bef74d3565c69fd13b8681d6a11c06d73dff9664c1ebb58254edd9a42b57c50d0e2f7b95fc10c669079664ebd96a6f6b7f4d002a77ed57c04d5df10845ec831a420a1c982975d1a6429b32f76e6317ec8786511386b681e25cb4398b7c549a0c0d9a732eeb5c6764e4f87ac90416fcf22b3af1440b73eeb0bda7a31c39cfd50515ca746a465382099ad154f57486ff330476565c51fdaec116cd06f12135c0e45eb7e09b570a61f018e005b23cf44e6ff57866c9c8b41fc0345299c562c4736f442ae214374c27d57b305127384f6246e3e77da25cc699f1154465412f4241252c9de6a83d1d3d3dcccf0698509119b402e4ebc99549d431000adf755ab7bc2e91eab518f9a869cf90437100a41efea719c022f1112b0732d92aa49a2e0b61579304f87fd5f2efc52e7c44e7833d768b2235c7a2d8f9d2b29f45ae0e6a7e4310d26798a38a7eb97e32fc9dbcfba1f7e21bf9558f9710ac719295105d1f67e56210f5b92470665106668ef157bdec212cae3e9d3ff5db6380510092ba62e722d8e15b63dfc208f0da6da7b7b8c08f1fdb332457579997dbe1a55656fb14af1665618bd99835f2cebd09fa5fc1e2c6e7b1623306fa3cef8222118a484d042c7303a487143b51e3f0362149b02d18386da67b3f2408a8d54600334f79c6ddff8fe36174a01050d23ab2d4824a4a9d2a83a72e9d7a5913341ef209237b71535b7642d53bd9221acf76ea745cbb20214b6329b07d4ac15e330af12ca05b10c4ee3534cd732724babb196b5fb9d10c6dc91e1ca941bce024206b625cf7d37d944300b328be339a3e68f309e9b49208e5b128530b10c57804c7dbb2f53769ebf98ffa07ec1792feca939be56afa2d3c507f3548896b5c544a36b3f001543e006128d8660f2c0ddbc1449c8abb1df3b43d20f1a5025b9d702735f76085ed8438f7206db012caeaed7393e2f38baca305a21adf1627f34baf1c30f080b6dee62259851650721e019d7343938a92790bdd6254529c0f808458a9faeb22fd7647ee7f88ac42223461145a13beacf792f2c1732413522a19cd8912235b20392ab99fdfcbff0730a4f09266760360248046179586ffc6aac16fee4959ed42fcb2a42eae6708f5321b81cd2a6eba65b4e4bda95d492d29eb4cc91a973b94604aaeef27e8d4380fa4348bfad2a5d04ff0c8741a4c6580e70b68e2ba9e109be048200f2f679401340331225721cb55ae25bc76cee9a2e91ec0fbdc7c52121d619e6e8b6cd9b22dfd385235c5fa74641630665df948bbd625b2392b6ddcae6510e57f9b5571e434d164d58add1191e991fefa22ef88ddb9f5489bbd2384d72ca237a23f5763adca7ab06789342a05c3a6ab31726d9af0bfe9fdcd54c3018e8292f74f83314abc69e0c017972849c43a7befc13a798510bcd209b693392e6c55a28ee660e838515626c4e2508b5f957fe493efeabd2fb47a5d78d933e77f1d36a22d4c6b8aab3c7fb36e74d09e8ba6c523b1d700b66ce264c5b282967c63865e52320f520400b50d0ef2a08222620180d77baee87a88f3277f9f07521a15526a40ffe656543e75472c2ed85b40079ea7f9f5a256305c80af89e1bb4f01c8268d02f7ef74c73700ed2635aad1020b702196334d729bdd030e68a4d8f5a34c32ddd246078d8a5730262a24d821ee291398b6acdadee7435f04c5df0c63e76cae163194fce3f1bc643d6b2fb4343c5690da8dd0b5ed100622e81565ecdc4c087dbce2b40d61120db9c0a71d36d0b2fc435f1da021f09df04f8d36d769bb321ed0cf31e43aed2037954298ef427efa3510b0c3c206caf6547ebe2df41480c737356b706e24fcfa855fa8271a3d0c199b3ce7537b1dc19b9e187bf9751bd1d451ef42c088940be2dc8ac5d8593360a22dc754eb06ebf3c18c15976f7a370bd14131f01127253cc2a46aac7bf0060a1ec47d431a6328fa2954d56da83cee9f06e4668ec07be8bb72ad9814f1be714d239172334b189076d76c73a58eaa5184b686ebe8101a8c697fcf172d2fc00e5649329913813eabae5ecefa0adb5a8dc104ab1618c050924147e8813b32d6b2801294e148358c514b2b833f5e3967fc39e887532c43015a15850353650f0a78495124ffaec054c8adc76dd625e75c044f71200e0d713017ad21288895f478722e00ac38d4996d0a8f7fe171a795d19746ec017ebe31650700f4c76eedf63c9da0a8dfdd4b27fa8a699c93d2a448f7d06d9dc52c4930d050ecffce1b87fe2223283f247434ba2ebf193c7ed32333df5fadcde96619c2039c8f6e34b099aca91b8fa88c3f290d6dd09f106142bedf0ac29c1f8c056d22a6ce85f45df8c5ad17640a9f790bdf9bdd55324c366fd72f6d75ba9bfb0d1be288316eda51c89921428ce04183c87bb0a9244fa80f44f03b973351ad38ab3bd24e709d2c7de57471d47ac0df964339fa5a72efc3da90692468046f3f24ea0f705e2c59642e41084ce3b47ee6790d2b6bd4375231bcf180a0fcae273dfa23ade15de9325fa0b39a74136e0cffba7d78cb5e0bbf463b8e089e5e62de7f99e0dd31291daf49a2a338755fc5f0a1ce1c3a9f1002bae3b98cd20aeb5a8de38b42f412e05e532caec3a035c15f9ea3f8db06561ec62ce0c7c0c12422dea24b92134820cfa206a98dd13a1b0fcb0cea8e22e20a3f3c33ac959d9df686c6b551bb4b10211e79312775da1da6a4b2f100bd9ab53fbefaf3ae09315e7a73f7e1ae3e0090b0fa21c0e42a1df058480618724085e343a58da5e825852cabc240c940f14fddc0e99cac75b8d9a5c6758b55d6e4f9bac10532ebc34d243c09d684e8277b99cb32c1a2d59cdfcca7fcee82b75433c2bf0df101951bb97cb6d631528d1497d3777029f3dc8926abb3302b92c0a8f04eb79b16f945dd114b85fd7a944bab027454b1040c01850b5af972a3a996295ef6d617d4fba03a75390f548f465adca35d0362ad013e1cc6760e9d018c26eb2b9f9c0d6a8bb18bcff4407ebe1b1835b17fc822048485770506df4d49cf421671a025f2e3db015310776b35a82fd2f586b21bf2860ed1373a2eb05749addd1898d4bcde45aaa34d4c94dc73a44a9f5266b9e311864709503a3e58518ce96a2c3e8dfc1e447609ee2cf76b6ca203a495c2b0125151c01ce091cd9e331aea74be7bad8fe0138c94b033e12da7c2c19cc8ec55c99091fb9d4006bf121c31dede198a0fdb9aa08420fb9f46eac4c803db032ec7c7318d16a34197350ede6c0343e321d3e860afc6fcd66ced27d0c13142518f054761d884aed3762b43ecd50a59df25c236e708789b8378409951cf05a14ae2f8c301e86e10e9a2c75dac1a808064a70a783f1bd6bcd5d1762e588332d3db71ad77d2c66895f66e4678a867b72d8fa053c51ebea627870628968d693351d2afffd301784ef0093bad30c1ba4ffdbf325c685fd021d468b962ef6e3834b1b16fc1a8026866eb630df467b433bc5030d03e31d1845fab0e2aad4e93678cae3b237e8751e0f33f6eeb7edfeecb02303f28ce4cb8fac0746f818f7b97f80284d2981894116758388812c470d1c522502beb11a2baa91bd7b9259977e6bb71cf538d0ba1f080cb65252c017f7d915cf72e1305cd9768c9cbc233d7a2cbac65601dd702b9b13f84d42fad54b4918a79b6f9b87317a1c84d61bdc347d2b2a707baa44e4727e00fd30dc95e3ffb7627e96bc745a782d686a6590d708e6b424ad1c38d32d78030685bf5d3407264c14f0f86a8db1bfd48a0ef5cc30565ec4af80f653686ebae92ccdc6a6e56fb0ecb116fe6bdb5e9b5df8def29e4d13ba2b4e1c43df8ac1ea591f708744d587ec77aa0f74434a6c0775234b42d67a303f914b2488df06d3adfb21dde903a21536b62058f8fa4e09577da8dced3d6a86f8c9493b47afb9e521bc2e0377854af2ae69bb1af04d10a8faabdeb54259dcbda8e554725b9f33a07c432a3e24bb51b780207643253c9de986abfbae1d89d751584a90996100e543d1482ce8cf358632f600945281f97aa741f687462fa3a23c1c01cc686030b029ce280ca0d82cea2b1c47193e344a6e4da2f3a256652bc22ed410f68ce3dff1894f7a1ffdbef96a47fa72bd8f73a38a915c2b099e0b4ece88a14bc60433cb8e405b050b234ee3a351f3635b76310c77f8886d3281c1b7a50f3626a2cc0ea25994d76d00d28261d65c0843cdd25b5f51f0f4e5c522c4aa467a5776b22d942e3e1de4601229057f6cbc81db25945b80c9c7762a9417c5e27965c09a3721087a8831cbe90fb7d1586f76ea5e296da6406828b0fb40b3322182ac14691b31ec674b650f030ed585df07aca077101dbf75aeb7d8832888831d9362571d9ddd452025455cf92707127b8374b4f589739517b47f44ed47c06f09bfa08e7d71e37cbbb95c9648300cbe5dc0454b493fc02b0e2babf80ac9ae3db7494f0cf7dc17e1e8ad12586001f75c172eac0f81ab8c4c2a69694dc9ca9442ee08444baf13bc42d3a8a9f1ed258b34cec704010c9cf981c7788d281f40d8992b1a2fd99509346da4e18068df280916a9553dd08cf6afac441b574aa35b74fc70521feb1044e4ff0651536c0f081ef95bf341fff600916e297ca6f383dbab078e848ac0dfdcf5a8857477995105d766a9d21236c4b06ba90d461862696361346329ca90b3a5e4fb045e6e0853277376295c715272fe5a774c7282a90b75068993786cd6fb6f11f671f10502ad01876b03a95d256fbe9d0b2ca9458e061b115c572c4beb67cf15bd0db54a1b9f2998435d14c36ed021240accc567f42705d1d96841da9e4aa166b30be259771a1eaea35bdac859a94e677251349fae7455a5df632c235b0d25e53e019da6ae7100171dab09430d4db4986c2716a8626194be4b67df0d4070e6a007acde0809f9012832959dad3f231e74c2dd4d3197830da3a0e2c48073e5cca2d41b5af904dd19078c02e4f2b4a2d0ab7f080b92fb061fd1cda443d33e9761d04b49b976141015c3d2e7c4e94871a724b8f24df4d4ab2688b3a0615bce95bcc6e919b1919ff3", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba010000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } -} diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md deleted file mode 100644 index 40932f1ee..000000000 --- a/circuits/benchmarks/results_insecure/report.md +++ /dev/null @@ -1,140 +0,0 @@ -# Enclave ZK Circuit Benchmarks - -**Generated:** 2026-05-21 10:09:51 UTC - -**Git Branch:** `feat/1549` -**Git Commit:** `1934c17dbe9cf5344fa0cb9bf247f5721a6d2f70` - -**Committee Size:** `H=3`, `N=3`, `T=1` - ---- - -## Protocol Summary - -### Circuit Benchmarks - -| Circuit | Constraints | Prove time (s) | Verify time (ms) | Proof size (KB) | -| -------------------- | ----------- | -------------- | ---------------- | --------------- | -| C0 | 6847 | 0.12 | 25.67 | 15.88 | -| C1 | 57818 | 0.34 | 25.70 | 15.88 | -| C2a | 142625 | 0.78 | 26.08 | 15.88 | -| C2b | 198355 | 0.86 | 26.92 | 15.88 | -| C3a | 132633 | 0.80 | 26.97 | 15.88 | -| C3b | 132633 | 0.80 | 26.97 | 15.88 | -| C4a | 92515 | 0.50 | 26.99 | 15.88 | -| C4b | 92515 | 0.50 | 26.99 | 15.88 | -| C5 | 151717 | 0.81 | 28.04 | 15.88 | -| user_data_encryption | 53732 | 0.34 | 26.75 | 15.88 | -| C6 | 86927 | 0.52 | 26.36 | 15.88 | -| C7 | 104273 | 0.50 | 26.75 | 15.88 | - -### Artifacts - -| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | -| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042552 | 176232 | 3218784 | -| Π_user | 15.88 KB | 0.12 KB | 2973037 | 170308 | 3143345 | -| Π_dec | 10.69 KB | 3.47 KB | 3553605 | 187212 | 3740817 | - -### Role / Phase / Activity - -| Role | Phase | Activity | Prove time | Proof size | Bandwidth | -| --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 305.20 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | combine folds + C5 | 0.81 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | 0.66 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | 0.52 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 79.92 s | 10.69 KB | 14.16 KB | - -## Integration test (`test_trbfv_actor`) - -### End-to-end phase timings (wall clock) - -| Phase | Duration (s) | -| ------------------------------------------- | ------------ | -| Starting trbfv actor test | 0.00 | -| Setup completed | 3.05 | -| Committee Setup Completed | 20.25 | -| Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 305.20 | -| E3Request -> PublicKeyAggregated | 307.80 | -| Application CT Gen | 0.31 | -| Running FHE Application | 0.00 | -| Ciphertext published -> PlaintextAggregated | 79.92 | -| Entire Test | 411.34 | - -### Thread pool (same process as integration test) - -| Setting | Value | -| ---------------------------- | ----- | -| Rayon threads | 13 | -| Max simultaneous Rayon tasks | 1 | -| Cores available | 14 | - -### CPU-bound operation timings (tracked in-process) - -| Name | Avg (s) | Runs | Total (s) | -| ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.11 | 3 | 0.34 | -| CalculateDecryptionShare | 0.61 | 3 | 1.82 | -| CalculateThresholdDecryption | 0.56 | 1 | 0.56 | -| GenEsiSss | 0.12 | 3 | 0.37 | -| GenPkShareAndSkSss | 0.23 | 3 | 0.68 | -| ZkDecryptedSharesAggregation | 8.43 | 1 | 8.43 | -| ZkDecryptionAggregation | 49.59 | 1 | 49.59 | -| ZkDkgAggregation | 20.54 | 1 | 20.54 | -| ZkDkgShareDecryption | 1.49 | 6 | 8.94 | -| ZkNodeDkgFold | 62.38 | 3 | 187.14 | -| ZkPkAggregation | 2.13 | 1 | 2.13 | -| ZkPkBfv | 0.34 | 3 | 1.02 | -| ZkPkGeneration | 1.38 | 3 | 4.14 | -| ZkShareComputation | 2.76 | 6 | 16.54 | -| ZkShareEncryption | 2.55 | 24 | 61.13 | -| ZkThresholdShareDecryption | 6.14 | 3 | 18.42 | -| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.29 | -| ZkVerifyShareProofs | 0.23 | 5 | 1.14 | - -Sum of tracked operation wall time: **383.24 s** (often much larger than end-to-end wall clock -because work runs in parallel). - -## Raw circuit benchmark JSON (Nargo) - -Source files for the **Circuit Benchmarks** table. Persist this directory with -`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without -re-running the integration test. - -| File | -| ----------------------------------------------------- | -| `dkg_e_sm_share_computation_default.json` | -| `dkg_pk_default.json` | -| `dkg_share_decryption_default.json` | -| `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | -| `threshold_decrypted_shares_aggregation_default.json` | -| `threshold_pk_aggregation_default.json` | -| `threshold_pk_generation_default.json` | -| `threshold_share_decryption_default.json` | -| `threshold_user_data_encryption_ct0_default.json` | -| `threshold_user_data_encryption_ct1_default.json` | - -## System Information - -### Hardware - -- **CPU:** Apple M4 Pro -- **CPU Cores:** 14 -- **RAM:** 48.00 GB -- **OS:** Darwin -- **Architecture:** arm64 - -### Software - -- **Nargo Version:** nargo version = 1.0.0-beta.16 noirc version = - 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: - 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) -- **Barretenberg Version:** 3.0.0-nightly.20260102 - -## Notes - -- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is - effectively 0. diff --git a/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json b/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json new file mode 100644 index 000000000..8a09e87e9 --- /dev/null +++ b/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json @@ -0,0 +1,11 @@ +{ + "benchmark_mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "proof_aggregation": true, + "multithread_jobs": 13, + "verbose": false, + "nodes_spawned": 20, + "committee_size_n": 3, + "network_model": "in_process_bus", + "testmode_harness": true +} diff --git a/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json b/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json new file mode 100644 index 000000000..7edc06ea4 --- /dev/null +++ b/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json @@ -0,0 +1,107 @@ +{ + "verify_gas": { + "dkg": 3042548, + "user": 2973097, + "dec": 3553726 + }, + "source": "folded_proof_export_plus_crisp_verify_test", + "artifact_sizes_bytes": { + "dkg": { + "proof": 10944, + "public_inputs": 480 + }, + "dec": { + "proof": 10944, + "public_inputs": 3552 + } + }, + "calldata_gas": { + "dkg": { + "proof": 170028, + "public_inputs": 6168, + "total": 176196 + }, + "dec": { + "proof": 169992, + "public_inputs": 17316, + "total": 187308 + } + }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "bfv_preset": "InsecureThreshold512", + "lambda": 2, + "proof_aggregation_enabled": true, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": true, + "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.124654569, "runs": 3, "total_seconds": 0.373963707 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.625217639, "runs": 3, "total_seconds": 1.875652917 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.556127167, "runs": 1, "total_seconds": 0.556127167 }, + { "name": "GenEsiSss", "avg_seconds": 0.166924514, "runs": 3, "total_seconds": 0.500773542 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.241305361, "runs": 3, "total_seconds": 0.723916083 }, + { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 7.62245093, "runs": 3, "total_seconds": 22.867352791 }, + { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 35.477445166, "runs": 3, "total_seconds": 106.432335499 }, + { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 7.549333777, "runs": 3, "total_seconds": 22.648001333 }, + { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 35.646490666, "runs": 3, "total_seconds": 106.939472 }, + { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 7.196253763, "runs": 3, "total_seconds": 21.588761291 }, + { "name": "NodeDkgFold/node_fold", "avg_seconds": 17.672638027, "runs": 3, "total_seconds": 53.017914083 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.409093792, "runs": 1, "total_seconds": 8.409093792 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 49.418440542, "runs": 1, "total_seconds": 49.418440542 }, + { "name": "ZkDkgAggregation", "avg_seconds": 20.366319709, "runs": 1, "total_seconds": 20.366319709 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 3.170409645, "runs": 6, "total_seconds": 19.022457874 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 111.173713722, "runs": 3, "total_seconds": 333.521141167 }, + { "name": "ZkPkAggregation", "avg_seconds": 4.325023125, "runs": 1, "total_seconds": 4.325023125 }, + { "name": "ZkPkBfv", "avg_seconds": 0.433745833, "runs": 3, "total_seconds": 1.3012375 }, + { "name": "ZkPkGeneration", "avg_seconds": 5.172915375, "runs": 3, "total_seconds": 15.518746125 }, + { "name": "ZkShareComputation", "avg_seconds": 7.412439382, "runs": 6, "total_seconds": 44.474636292 }, + { "name": "ZkShareEncryption", "avg_seconds": 7.468529083, "runs": 24, "total_seconds": 179.244698 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 7.677126833, "runs": 3, "total_seconds": 23.031380501 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.114853486, "runs": 3, "total_seconds": 0.344560458 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.24184635, "runs": 5, "total_seconds": 1.20923175 } + ], + "operation_timings_total_seconds": 1037.711237248, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, + { "label": "Setup completed", "seconds": 3.042068, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.254953375, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.005661959, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 131.583918, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 159.570454958, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 162.126205083, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.3074025, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.003480958, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 57.874201, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 68.103220875, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 253.849896667, "metric": "wall_clock" } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000ca0bfc9448e9ef16c000000000000000000000000000000000000000000000002c6fc73ad221ed124000000000000000000000000000000000000000000000008224c7cdccbb18af40000000000000000000000000000000000000000000000000001877d7b0fe868000000000000000000000000000000000000000000000004ffaa9844e438d4d300000000000000000000000000000000000000000000000ac1d3924637ac840700000000000000000000000000000000000000000000000b23147b92159485ad0000000000000000000000000000000000000000000000000002e033f6bc040300000000000000000000000000000000000000000000000768068eda5eb7ec45000000000000000000000000000000000000000000000006b320ac7697a7d933000000000000000000000000000000000000000000000003ca3c43312f4a1a730000000000000000000000000000000000000000000000000000b90e9ebd4813000000000000000000000000000000000000000000000002d7efb8bc1720f5e4000000000000000000000000000000000000000000000003f44e6bbb5f7dd5e000000000000000000000000000000000000000000000000d73b5021fc0617d170000000000000000000000000000000000000000000000000002138f4eea500814ddc04dff7aade6906eaa66607c4f4fc4b7fad8d7d6dc1781b1ccb5824ae1460e35805f5cf672d03d0c6e86292f9270e308666e0a68e54ba34c48c7d1c1171e08ab78f3b3a380edceaf2be1332bb0f6970b24f889216fb52b96903c3672078e2a2d8c18c4b84f1ee541265621917acd4fce05eec99dc817ef9a1b30df545bdf2ffe8839de28e724ff17364a02a9190034ee4093d0771fe513866af5eee3e16c2640fd2eed50030bcc39cdfc2089b3af2b2fc2cf47c558c544d264cb316924fb06cabc1c64e480876c03a96dd26e2ec80e2a1fe4a060eba855017ac571e27bf226f2d314aa75307227826f8c8cc3558237c0c85427b4053190b5a66b8518499a04c8896e47bde11d94d474f7eb7e24a697ff050a323e51605c0a2f3795b43a55216ecd8b7dbdd337ec0eed71fac7dca88403ce3aad06ca8e1f5804dbf2dbfeea2d01dd0ea69cbd98b44cd4163166c47696b0cb64815f84169ca91ff184bc2aa703873167206ba8215672551c6c39711972e0fcb6171286004706f3f61d7c56d50f286c3eb1632be8786c44dc777090a9f3b2528a91259674a958b8287d9e6e7902eda561c95e35960e5fc1a918c47214a8a3fd83999188e9cf8c4bb3af86a867072be6dc0e4cf33e535bb85259a027f564bfcb50f2633630222848a6fbc30bc81f15de7cce451611df563628ebf63c866506617a24c47ee9943936bedf6006d403e517ea950c046b99c76b6a2f049de41634d85d23ec7fe432a42c24131a81e41e87bc342ff0f4a94a49a0e42bc24fab0ad8569dbe45de50452d2f1518f6964d106282a56209e9549419539fa23a7a21d5adb558af735bb87d547053b4b077b328e1943bbf60c5f59f31a56dbd1bf144efa369f33bb0c50f7e45ebc416e850d2241b4c910649052f98c615c093e763eb742f1cd429f36c4824b3f875f18c45581825b2a7c498228972f28cc96b5c90ee1c1dbbbc46d9ffae4973bccf711fca6d17a07d52ec5c6aed0fc3e9a064c37ade09de5dcae4e637f46271f943e8933c50061892e9ad08e8bff03cd7eddfc9ffada674bb27d9867e628d90ef6e9fd19ee92222e6a3072e0948ff4d178389a49a145e68efdbf306e35874e3ee27452ff17028fbcba83976dfd49e75c8bad82fb9339e77aa7fb76af7670b90e084210ebcc223d988bb703c4a113f7131e52a583d944668b72c08fcc4f552a6927dc3c7333b06b41b4b298fb5caa9170db380fe81195d0da8c38d6de7c337c52e5f024f046526d3b8bf4befc00cc172fde6e835ebe761c7af500c1a3de202df707e91201f7a02d38fa8b9456f6777eb006c1834afae72b3e500ffcea21a36b82e83a704da1e1863aaf8b58a8662f0dcb6fe6fd3e35bcb4bb922291315bcdd7d2f00ccc7db3d20e4d6112826184c37f83fef8bbe495aa3976e2e8c334b967ce4fc0a94d26503189d92a2f7b89fea1bcef0c5ae1147d833e4ac1d52120b36cb7d10619527140b27a521e78a3d4075e127c21300306256edc76d094502551553efcead0caec3b1097621f0a820ca04263bd57e7afa49cfb92a50eeffe2ac26f7502a496adfdf8e23d183479522e4fd838ee09a8fd50fec4a22103536fb118a251999b16042603d025ddd4670df746903205ec04aaa1c131a8565298996d2ef8ae601c995ee0e1c27b2ee13e4555206067c20ee5e054c793933fad112f9cfb4edefa8b6a5cccc8a01fdf4cb41e6d9bf4216246445428daa66e8263a65b79139ab1011ef9a150b9b1a40b8798c1e4e008ec02795bbf95bdd7a531a94ae8df852e92b98e7766fa3ad0c79d387b21791f763ca553d7d225632bcf58e60c6c608ee1443f98f979f9d3603afa7aef8fb5c103ca78c01bcc6917d4f1e0aa09bf3ea417c51c1c3f9ea2b902151931b21c9d25e574019fb092e03c40399b0b8fad6e3347f5961674c366450049609cb8eae6ff4e987806a36851ec132675dfdd390f61fc185b808b31af6ab220359a2710339887cdb032a26cee0dc574e33c1651f7e8b746d016f39fff2c605cb5e7f0b7a839277f904d2bf506ecc02f74be5baa88793d3265700bf8801312174144bf5372435bebb6c3af3d31bd15519858190765d99eb03caa1b516aa3a219db366834e6cf2d8dd47bafc50fce17f23a9556dc30699f727f09edecee94d252a7c4d5498c27a3915b7ac677625ac6829b5777a2dd234491d87168bccbf5301b9221ff82f1648b3b24d2e107329c0a910034296426f76ccc9941c1267df6d14d180edf7ff58f8aafc9fddc2b6a9a641e608c5d6ee03ccd615b645b17775502ac1b479e0010f878c96dcfb913639debcc8dc163faa63994d6fe4299db056f125cbabf7fd62bc792e6505b14e5849ef4506bb43babd4cd46e9df6d402c7e4de2e213bf9ca80aaca06b1baeb5554cce579df7cfad676200cfd2518592c92c6810728dcbf5a308018bdd6a9d0d6a3249cd59a8725da15de0a96a899f510c5407711b0753955e733c32aa3b780c292dcc4a8a24ff2f7bb224370ad0b0c6fca1f601947e99c3cbb54eefe69676eb12aaa3473410966221a4980511010e6f3212c281aae2b27f42ea11fb627fa4c854828ae3d1bb27551fb5ec75b54e342f5cef48f0d592aca51491f88b890e7dd0aaeeb290d165a763aa3b7f3b34ff65fa5a05f8f014f6b3daab5bcefdc03d0aa1f38464f5b0791d8e1f379669b72f548d9afe3490cbeeb0626f19b784265812c1f6161978d27f9e9c0349f028fe3b71197ef130c2dcf2eb7d5edb0b18a4194616eb35c7d614e26fcab690613d9757b323d94867d0da5034a534f65584cce55bd9c5119c20d2b8431ace83236e90ccb7be0c387b11b3c916229a3bd8603bfcb9e293b7c9a2363b898382d57eed13d5ed795066b07218dab59a3ffb999172a0025c43d200d12d8a155128c4db831c24560f654e43f1a27266db9ad1d94700efab02e2f0d39c6e2a84f02523fb12d290ba50e9a1a9805b38dca8bc7363b838f11fc5fcc4a39acc6c72112807ce045be538f43e9f27c01e75d05c14f7596940ec764feb95e2e28f10390a0e74e5cb3f16140127cfa6a156fa560a3c6e13592c8cb5acce08db1c9874a24e8a9ec69e453fa9f1384d46b1f263787814072fc03ea72b8abc134a205c058e5e9b963cf4b4b043993a01abe01f4245be8b9d7f9f127e5c248ac6c5b5dd7b3454e07ded8374360d40d75a39d1d609f99c2bcec53998e407d157b88698bf2b4c830874dfa537b5660303e798d095fe35d6c3ccbbbd131305128dfc78d99bd02faaca0f03250b0ba2cf8f859000afa115aacb761780b4ccf0eabb203038ea0ba410b2444e91ae6a10d0a729763234c408b6cbaf328d74d6be6bfdd4b4bdb74bde0e49da02911bbac860f7b1ecd27981f9ba1b18d35d4db16012579d470cba0e5e117421ac0da980fb45ea23f9b1f4bfded7640d9a41767a3819ffed3d0b1ccff98ad78f63cb4548b6c487e3fa90a8e68436d8db97480afb645de0e76939f6aec3a9183ccc113ae639c4f445b4c161e47279af3abe35b80d6a971ae876e0bcdf15ddc455da007ed56c493de38182b07e1f76681a2fa9196f45c3d25f98606688b55ea6336e19638711ff00d788f16bab3baf7545061ef03f59fd3b3c69a5da63683b801868689aa56c890cba7640b3ef1806aae00be03157081b188c4bfcc045ddd65d1364bbd2f73d03d5eccff18cbfd02fef55f8259a3abb1cf3c80dae34193e155ce2d02117b2281181acad525abfcef8f617e08f3264d0cc3676b330037bef0c76d2be781d9109130dae3a2064dde40ea9103327dc858625e5c5f43636aeadfbbf08b766607aea64758115d12f3a3ccaa680c8bac7c90675b26db2aa4e437820f8843f613f9f1bfd8cb4a431bdab7ae3f2815251e6440f4575397b1362413dc0d908ac8035d7e0dc154462102269ce752caf1c0e965c80463937edade5f71dbb16559824a082fd53c65483e06fb749bec6adfe9659772b8db6d26541bf074d751096ad81e99c52d7ef4102c2646c0a220aa8b25cc7fff384b029e841c5e1d1a9663e247c9fb35838a44322b00b86c4f53843909921594d8772dd67f6fcd57b555453ec0e6a2914fb82b4f9e2fa795c99591a02f6cddcbeeff3995a26eff6d038b7c063ce6b7e95f591130710c9a83eb1d4092d30b6e3a0db2643d049eddbb6486ca3ff613045343ae99a6af052b868c84142535fcf42cd31d9c039d599e1492cce99d4dd8439330b0a2077f165fc7e1fb0a27e24eac75faefb6ebb8b825e535423f5663b09f7b157205c0302151f296af0b1d5734e1e2a8eb3cd397a8f7e9cd015e333ce73c33df4bd49e860bda526af203f82dc6b615c2ebbbaa3b99a13e45f231c133bdc755807b03311123d395bfe1a13422c38063cd2f8cc6604a3ffbad19e5bbd5776668f5b907c3f72150da121fc376b7231173de23fb4b6d2edc5989dd2e82ae695cce929259aac229968d3d266b9913cf2d577474ccbda946ddc4ad52cab0388806f364246982402ca8b651f56a0f593424f4511a776152de94b2d5e040f23eb6f50d03029a0731056d6bb00954510884059b04e580005f131b335f45dd627242e573447d24ca9317ebaa00cee1d6cd54e10693d4656d19191ea9d63f1fb58989b317a7bb4f173f02154a9ad7593c3f48daa36a36deaf60a363365cadefcb3cd7f7eedf78e07817168d8e60ae9532d27b0eb422367876aa2840738a06d8487727fb74a6e75e44f6045b19a874d8bbd97efbaf376dd343648da57e8da19a975e3c715af62bcd12cf1b40e16223afd640866cefb3d7dba53e2e6a306634692d558fdf6ca8190683c8241d67526488d47450901eee756a9ce807cf18f54e62cc823f5d47b4e3af46ca1f3b94e88bd4115fd91451db59a8e8dc0c111024a2b38af1deb3854d2994887b1037817024ab3081acfeff6b50282ba402d85b56966899fb39a6c01df6771f33044b41a29308e89a75b06b6627d85cb028d191f46bfc52df2723360f52662e99087a5737c61ee99f7cd057bf2e18689f5b747595ff1199ae6798b0499c8d75e113362ac3d589042b46807e6fbf16b6e40c8f94e444d7652dcc44d8984ad500cf1d29a2242bd30bfb5bd3eb76e7cf673275c21f9ecd128d346760169d28ab130b042b4111792bb700688ed4c313c5f168776bebd4efc7b5c8a244d8ba035bc42d039278015f319ef204e7869582a42a617cc51566356ba50ed425e8a4b749d81a0ad6f18abb1f53267cff6f65e6714c4a397efb80184e037c6327175f09bcaf7e28e8c9aee1ff608389deb8627134d0ce9fa5feb55c170e782d5214ccd605eec1257df978c84c72adec12e2b0f3126371d1ee637532aa455e2d2a78d62afdd7180045f5626941bdf1f7849adbcb24000d1c9eac6d91adadf387706d72b5dd34e804d4b52193f0dcd0d219a9063743deb5f4349336dbe6b49dbf416c1789d3d07d304e7354d6a54ead2ec2ba06b67b345473b6e1a9b1c26156c39facaf76f899e428ccb16ec1924cb31fb117f22bd7b131cbd6ea732abfc50459469e76d07dd90f1387549c3b417078677264bf1f284a099584aac71c07e7fb7363c60c868f435d20a32b528b59ce742f5afae859176c4931b55e6c0fd7e3b34fac1e1fc4116fb61da0d8cf4cb32d83588556adfc358e1130ce98576c3a350612bb36da466e5e430cd2ee1202e4ba044375f6bb5082a688a43cbc32fa463585c9f90a8572910d9a06c16854bbd5268dc754d94d49f89542e8cc8b2ef418ceb21cac5bd74938c7fa1da3952a91f4563cfccc6eb1946a3ddf30c389085a4ab95a232b732e139e86200d1a9770453f3cf095f35f9ddaea29d99de184b8123a5f2b63c0e824c16207991ad29daaa0ee898629c92a646495916aa6fd424d5015b85432cee703ad1b2aeb038313c5e1410d79237e6ca180f40c19d5e5b2513a1377ca2fac6715217ba9351fc5b3942d314b2e93c71a2f96d9155db47b6d78e1cc2bd004aa22003e163b6412a752ca2e7889cefa2751358fd0fbe3884d99f106d68c973c1dce717a0f33eb02837031a40957b5fa4835b44747471801e52586ac30295db9fc1bb1a704f18b0d1447924bf9f1e02784cc1b0532824a4c078107acd6fd9db620fd1a61a0b0ee0aaa2950bab91529b7454b666079592dc6e88fae0ed90f90d35892225d47a4862f8963dea3b555a8f01f4da08bd46ae1ae6bc16bcf7005347c8c19de6c2145ad125aa502df4cd04faff782bc862e046cc3240bf2c8bcd04f70bb4ef6d36fa27c1677f6bd1cd6b98983ddf5012b2f031398fbf6ca5c8a32da32cde2e62c75369011f86008c2d4c0ca000b7ad4499d797d6b91fa77825fb44045e75a9216249177301422c032ca94ed2749e37ac341bfcd3d25fc7ecd745e66051a06188f3f4a0b07383c49c3ef429d01566aa613cf191134cf3431342729cfbaa7732cf5482fd72aa56ab908372599856c78c0b74632d5d60966806bd67d0f82f96c58df6256021c260bcdf988a10981f18558f30407b89644a7ea76d78c0ddb151d55530b667c1343edabed74cd778cefe84306c1eab5bfebc5d97b7791300950279dc2aeae2f235b102f73827cdd964704d11d5240f22d7da11fa6afa07b841291a2c1a7ac492c757990536725bda41c4083e8acb511abb6eddeecf6e50539c4c011b3d1d10a27f1b8b43e24e1a85a932f807aae6be08145cb67c932c8a20452d5cdec07bb06169254ee048b3edeadb4c9934efcf8527debbf850f8903614da37e81f52853002fd4cfb00556968925d6b81c10197dae197b9ccfece01494971e15967e157e662a022c3517eba2f66934d09cf0610075120f622acdcec3064a52150f6d095a2724e0d64d5824673d815ea1dec20dbb5b193705c78f2b4415be9e5c2ce67e2d1d0846cb6a25cf5de0808ee27abc17f5508b07f104a3f3f0522b6b226f4b7b8b8e0e999218e897f2a6d36fb1f2dbb1e4b57ce685332a56be535c6c6cc5d257e9ff05a455f3cd0d1f8ea76967807a251b0bcb3a1cd8da51868169fc2f727c528545242850473b17de1e572da6cc3796b98847e836d80bf2ec2618287d37d544866004e27ac002f00d5ff8399f2f6c541c3afde128584ec229ff1bfd92b8fb2fedc826a12118800da2ff24c92d5f7d15f409cc23b8cccef62cade5ec8af6270aaac10e4c6b05214f6fb1f74325f1ea5d67238b2789d21a634c24e687a7636b7ce8590d2c3adb23cd6404145afddbf5e18654b398ac0a0ee354b6cfd254d9971412c302b57afb73e644896ec74bdaad3a21c268c48b37a0cf7970a22e14a95f72addf13af72ee85e567fb98daa6d94ba88d6c8af8e02bfc037fe0624c438793677591146cd48b3da4d2330c7cc82be58818b5c96ea094be4a314c8c1c3023340d48f417fdb66ff71bf86748e95ea77dfdcd27d5593241e172d745dd8ca6907f47e6b207103a1fc9bff0ba9cc5556127307bab4dd8a9cd71f62b579ae35854b736af3d2e7648230877b9611da0c0e657ffd48e0e7982e1489b6a9a37943e6cb947bd2e0abacc109c9e01f390f28eb2bc2264e2e865c46662734ab8a718d3d812c8fdfc04357462790a6d2f5ad09b11f9c4954bb8a61e7f7176ffae8ddb6733acd31fad0b5087afb2695f9eaea944cfba9505200d4fe8788a724e4aa60b165b54c35cfc2ea275976325c84249e203d793ace9ad8001a255ebfae02f564a79ad3ef9946c2dde6205a2c3b0fa8cd8e0b431bd3b0ff968a9a97cb61f2737fb6ef216ac4afb121d720587325db079dc26956af3712406d0ce60be27365339434aa7b9ee129001b0515f27e5434b9ecf0e445ee0a45d3ccd7774a54ce036ffb5177730971fc814a28953fad3b536c531909042ffa1677de8fafac8eb7db383dd9a88ea4527f70de0966b2d3bac048cc489c0c0c691913e79bc4f3ba6ac385d012978628ad122098600e73b68eddfffbafb4022683ceb1c5d689808d8f5a5fdc8ffc4730eda3426a7caf15d866e5284890ce098709e86666e178cfd0689f4d1a747666fb7e7ac084cd90565505ddf40fc09aa3e8bfe26c2db1432e60f0eb404aeea8e4249a6960a303c3fe143cb55e45564fe5b0913bf8f6c982c4c1f7649985707ee9fdbda1712afd04b600ab380d70905cee858094ca77f0b8a595709a670dd57b8faef6455179804b0479438dceab774cb4d960609046dfc54837771e26f656d613a1fb1831c84c7318ef1b588cdf7d909e123c20359af82e3d0a9bb9d1d26554b009804462a6934cef992bf0765f824d9986f3aeebed385f8731ead30635a6ce040c50618089e2e038bc669e32c3edcd131748319cae641a697f5566484a5b5504973d80b1b3f4cc0d1cf90e917b6ad033ec67e2604689987b38edff98d9ce4bf410f07f320b79c6e4192eec6ea444dc7a96af93823e5f114a0a21b343b8b6ff1a5f767ef1e0b9c2c1e64d85aa608eccc55e23d1c485f9a020aef106f2d718294e71f74ce1e49aab051f13f1142f9082466304d55db810f59e935482b661e03bf7d4200a9033ccb47c4871c61b9a42198ac6093d8d541d270d61e03a20ecad6e817cfc0ef30636283d0dcf39193aefe762da76c6e097f0a739c646ae0aea623666a3c85850c4f1220292cd457719f4071a133247a33eca153bb23a86742cd45e9f662cfc70b8dd504c56ce14ac9281add81dc778f47d84a27cc8d9560089a06c0190f50ef0bb97e18ce7eee5a4b05824fc996d8be0be1a8607a2dc26b9081af9f4c7cdc080ad4f1362d634c656a60a00a633af090d6e88ff387f306dd112205a300be4b5e175a0f41ee2865da3d79bd45526801e5032bd937f0f233f1ccebce99a43140ef234c7aefdd9c4a8f2644a9737d9ffcc545c288e8532d239c5176e230d3b93bf805c0ef1dcfffa75d1d2239524bd3a31b750b503a1e80c93d53457ab6aead7905297ec80f9ba9766b86db4d469ebb8e722662bcb84f4fb56e4030d482e9eff9d425dbade57b6986f552847e2575ae031911abf1a32e564a596ed576d3485de6401df28c94d9f4881155e58c6619602c2a468e27ba9b3df8c12199bfb949cbba410075704eba55021d5b95af43bb0cf783f84504a364fb940de65bff7eb7af405023a786e30bfd564da19f1155cc39b2d3f2a6af67b3b5204687917587c932b06a1deb041c5f0aab75a685874b5e6562c25c68e73155641b3e66e5567a553a04ef0ce570707daaa4b832abde636ff8a551512fd4c098fae9788026a28929ae4f3d09fc6fc052215b7b72f281ad73b3d4ba061286a713532fc71c54d264dd88c40a139598384a5b5090ef33fe0136e29eaa89390f81f3f46c2e61bee9b3fe89ad0025fbd0659972ec4213a2c76d87aadae18718d14abae030c5604ca57dcba8743b00bc5ef116254de78a4bbd1cbe1c0a07065296e671bcb8799caf900f24ea440f1ace537f57b619f0e5af6ab81ab1f229b22e47a9f9c6f8ddb4af9672e142cc4a172c915ae763d93adc9d47aacf9ecb9564e662c30741ec9b5c53ba3683a99ad800cb93318c412ecd8cc68042f1b2bbf47a3057d98ccffa1119780ed2975d2b031a9e72eb5031d48e4f38ab89ec302f2680e5bf6cdecdb20d9b9b3045f14c414a04ce171fd95093b88239b7b7d2705e385c0f598c8d188fb51f46f9f7405c358a2721a2fe172c8f37a88d02c0480694867d40e734f04883503f7acfff6542084c17e4a9834dbbb1f0a1d06ebb8edcb614e50f4d9d69762f94874d04d6fde4c0752d806a1a856674ce00b07a6f0db2f0406821ffe52ee7bb9eef56faf3b678a70b0056caf14c48cef88874a5d02c859c85256e1015202caec5998878c9acc0551d06eb603def682de60f7f0a6b42ee075b80ba4fae308126cd782d7907534fe71912dfd5fff73789282bdabdc9299d7baf436ca341104bc53cf83e797aa7f785101e9d723ebc25c4cff66b835fbe4041916e7465104f9b98478565eb0bfc2997fc0567e4fd5c256c85f68fac3ee24e9319f14d2405076e2a72bc71fc5f068842a71dceac206bd01bc8e72cb790af500bf02e65223546cf33ca170765f805f944070a46b061b5d11b40ace050d8cc5feb16f7c4c9b438eac11ae5981a0618f1d45c12caba43cc1af67eee753b03836dd02e4086eeebc7383e62ff80393178cddfa30efd36409f3cce15a1f3ac25691bdcb29bee5f05aadae6ad7338c675b2c4f96506539f855e4208731d60c46aeb34602b935a8f721310d9bebb0d1bc55a48947829e0aff2871271b07d65be03044bccd3983cc2991e66e1d4fb79f4a45307913326330c5496174b12a1904fc8504adbf956d3d00caf4d042a1c23d426ddfe81de04110ed4e9d82d413131da701c94510acc7894d2c5783821fb1b2947cf9b9fc815c703538175f3c0cf2faad70dda3af3dc6967bc4162e5dd3f398477700f110e24187e1ba95407833ed314bb24f38410df33bcab59155fb173c172220aedb46105fbbe750bfe20184c07aaf6ab485047c7d2fcf738e5519feac385e7d74e472e22b580993c10199c9a264fb71fd89e7cd56eefbbda1cdd63962d7d30107481532c63d7f82930de9f0e3e2c7affb5392975b7d7744702e68b1766ae7de38f60352ca38e67916736b2da3e6433726cd27b7f17e76769b44a03fe3c4e2cad5379c529ab3694223c72b67c02c60057aff7003b3e53d1ec59364a6fc561c7b4b184db2bdd79fdc9704edf0916ff4740514c1987e2f2168cc54b9dd340d02e259f99ce01ad9d5d6944dd52a978e6ba9bca1d277dd21ef3f6928cc2297ee6e76ad3256a15f1009a8956ccd68a338c2e099e5afe9b3365e5818198cb107a9f70be8add7a0ba76731923e8dc81ec699a0ef0fee7451bdf6fdd54f16e3c37bbeb1b23ab07d11a887b6a1318ce8ca89bdd326a988dd00d3b4ae59c1bbbe540469b4b3794666060925b7acc6dab414837c03d8ee123e09f87cf8a12f05c85f3ad1821edf8d12187339266d5d2a9bfa63eea5604415e1d7dfcb7cf9a3df030cdd014c5c615e340cbca315a89dd69f61b352c60c2ac8329eb51b4cacf86f3c467c0199eb1199f7093628fe7f809ceb200f8b262781c164f9e96ddb9ad897fcd1822bee0bc8df832c92537cf2cdfbc0e4d83553deb8249c5eb8bc198f1ad13f00f38e855883a4812c8692f4d277a5639d047410ad23a154c3f18b76e4b062a2f82eaa89035615db2c1f23e3ec233ad71196965522f67120d986e10b52475585d7398575cbc8a81626024b8181e049e4889d5939fea09166d9fe502e50f8e75b8a39cfeee63386890495f31496f1b60165d7afba12d993e5f3dfd22b89a1af0fb254d1151f1b263b2cc367575e069b0d0833cedd493be67d216fdec70fd7e4ea0311ffd0c622838c02297e7a5899d85528b3b21b8128abb3ee080f203fa7ef135170f4eaffc9e1fd0ba8712161528fee2c370ac0f78a6325a1f64768309a307def9f9192feabee851b6ec658ab933729028cc58961923dde03438a74bdd19ae209d65dd2a2d1b2f801c07e1b41644974aa3a9de8bb0a0054e1e4e494c2330029e9121930c07e1bf8045674837a5913cf33bd338e0903b9fb9c99a63584d83fdf0ed16c4174334f4a18138d91a0b174724252869878ed251e5dece675925eec92cdcb8215e5e4962d21989b001f106ea3d1a5143133cace9939d4bfd6b263bb3d9ff7853042f7766118940257dcbb06910a1c5809866a3b2d5d12fcce6ca65870c1f9173ff5c9603622ff578cdeae9d93d70ef9f3be40e103ba76d08fa6093dafe5c1b5590a63b9e6304326fae968c3e34f8880064566f2a96ffbe50721f2b5da52d43a5188cd710b1aef98bc4b71623fbf42b824b15a39044f7c1875e2be037b0918772b3a4b433e22a71b722d0878620af6721d59a1053c50234f4f03ca953a37a156fb6170f0190ef5bcc4e73421f17752d6857e24e8a6adb26f80c34d96bb8e613ce3918d4ddf2ab74e92651bf9c4c7be3eb2ab58693be33a8c3bfa0d51d077d9e2feb526241c058d33d0d04a34025d6513192d57dda67ec1800bca63872ed03c866eb1942db6256ab2fc551659b6a6ac38c0d3a218537151ac2497e6ac5d1a6dce982811fd2c2f72c2f6ecbc03420498f9d2d19c28416b931d31eca2a50bd70286778b5d5b3e07a9fcd192fc5c1ef2b71ae66a29d9b86c423a24c3f3d6fba8e8b6fd3a3ef2e621be45cbe7c8a823068cb6169ed79d25885594c68bb5707a95543a8dbc98c83d2593ebf226e6fa831bff91f121397319f586173e4fccfc25eace77d338669586066c1d07f923cda6df3e64acad98fa0c9bd48f3348d5891d153d215f0ef0935e04b3d439f7ae1db0ba9c09160acf0c2dd57dae351e81a229f10550f78569c69d10e0555e194d2feac7c390651f902cdc61a031e3cec9d194ae5a1abd14ad9fb3155a16e4c965278c08c26a4c65b9a7999685e602dae2f686e6eedae39af4cf3c1991c34e32bf7e092b14238f99eced1da6cbb658e72f399c5e2204cc4517284c2f2940346864a12b66ed991e5d9e54daa380c27ff63265545bf809f1cf6040c62d1dab6c55cbfbeaf1cba651c23b6e4183365896bb6a75426fecb5d7e8588625249f65de21a1fca4895de7b13d96debad13bd97edfce751359d75e90dd0ec69b1086a85f55e71d8c65942a39cd57b3ddfc66d17720f40935dfa4041ac91b02ee2e0b88602eb808552a94e2596819a2bd322aeaf970f0b9f25520c979b5ce334e1e4c41062005db6efa42f26b3afe995f3c612a3207a60cf0f86d9afb2f5c3c9e2b98ddb4cdeb681d7a79a2a9e39ee354e55db4f9489931d684aa43609c8c51b02232d661cde0880715c6d7b0d9bf48da01e99f43d720ee4b6d06cf67d1ea57472ad7287de202f92536196e5e4e7efdc289f2e8d29dae80c0ddd34e43b06b700010c6d2dd5eb40b30f5c1f34b1cbf5dd29a9c6413b404cf7f4d5e44c6efd06ebd2f61b00c6a9a23c2ee4a912a346bdeb353a24bb54066c2b48f603ce10bace6fb2b6b2f67b01b1cdf7418a0aedea179e0420b190977ad849bbf763ea19ae22a641e5416a795908c6a5bbf2fc3376ce7c9a189e4852d0a59b1b31cf12bbc26a43c0540859ec14f887cd35eade20a86905e21608136a7b9225a5aa16a9a55318071120ea66796e1e296f0eb89c9dd8a77ec55ba003d439189622adea8684f9c0d7806f7f6a167a329588ef99908d323e4038d7888fb2fd558cca93ff8a8e360148e0c47df63964e87a2f9e5dc456e9371cf436f04ad4ba531b2560a9160fcf8ffe21c8a6392a254c34a14b1fa07415cb779b4a97bfebf9c856716c497c04ee020dc2ba8db6e64064f6a12864b950b4fb8943d07c192c6b3c64636d04e920242f0b821e1e5e9468a4099bb85d0dd20a2ca0ade28850e0c75a29e17d7ea104b967dbf1a0fa7c3ca7ee2fab640e3993c5034cc9db10e99422ec8393af23fc1244823540c04d3650598e70a47652d82822dbeb095bb5422a85ebe8432d25a4b6c4bf1ff0f6522a9ab211ff1043c79af9f816db1a3ca129f5ef5b11c482f7db11867ed3405f151fedc4ecfc5d689a334802fb029a00bd9764908c84397b8ddc832d2a6ec0489befbd5fed24727e2449e82ac9a1405d9a21cd2f57451b548030bd8b00cb41b999bc3ddb385c86dee4bf26a1c90865a9cc3fb683378cb26248fa87424483120ae64deebee903fba7708211f871846015acd4d576f03d8b04662244b5a64260fded3e599ab32a66c2ed158174d3c791cf146feef2486d3458fdeb49f2d2acb27580f9291d1eff894607e2883f2efd696c8d34ac9bfd4a586b8fe056fa28f6e10aa24e33810e8b61312f869c995d4aac65dde2d094795668d54b0dc219fbc062128339cfb1a949f000a02d46388c56437439dbf3d755e85880f52510c59882120bc9892c6ca86d070eea1abe8c2566f701490a9c58012eaf3b6d6d126927348280e5413075437951f5f113bef1d4df52f41f45df426552b611545d37697ff1f0292dafb57a127f033d599a0e08071988f73608d79181ea1a4bbae27414bcdf315cebeeca215eb368de29fcfb40816567217203a1d1597dfdcd00b5220a802430a9ad88662352c218a6dcb7b7e081ec4fbd5bbd0bc983b48d405bd40db07970719874b5f0f23d5cc8242a11f54eb44441ad3afe5b59faf20a86bdc466d2af63113b7f9ca89a8532daef2d3d094372129a5beb0fa710ad73f7aff437585bfa2f2153d127117468e302cf735c858a1d9249dd51377d0c49e5c5087cf8c8e859e80187fd2efb29c999cca326d9faa50bee31813c4610afe50f2e78e140fdc05227c02223b0524a911ad80988b7784337087a40dd271d329432802b6eac492e7e57a0193fac4b5b1b03a48592cd4211acfdfa0916177c1ad875e3b7d60bea7f023b72dc0b82e002721ffc03a4e8aaa8bca823e3323373116bc9d3577f80eed7df61521e0ad3fb1dfab09b965cfa000dc910eee2e99a86ec7debedd1bfa6eba05844a1a880f9155c742f51e7b96419d512e48f56ba3c42b5d7dec49c3500b16c2084c025f86494d8ba7df6789e6ac2df2131f4ba25d5d554d75b2cdf1eeee4e44d6e421d8f4dfe37685b8da071a2112f4d690cff5e495994ac9c2af79f6d1244f984427f1e6c51dfb2011f9d1154ba3d84f2b23b52897c7f2f4a10820195af3d0be462564326ea6a7a6d7e9b4131408df2c8207e26410fbda6e2de4f8977c53fc0290", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da04570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a7110ecfce55cd472966bf92d95eaa2ca0aa89a06129ca6ea67d7b5c9e26e4925e01de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811088db0dd523e2dda90a02310bc443e2796689bb889f1a91f22c7e910915558a7279cc5790a050ab4e07fa5176b9018996360291d94e5cda28e417eb2fdd3e8df" + }, + "decryption_aggregator": { + "proof_hex": "0x0000000000000000000000000000000000000000000000040ab06fe21da21825000000000000000000000000000000000000000000000003d09f6ba158cb416a00000000000000000000000000000000000000000000000653ab0fe8456da7cb0000000000000000000000000000000000000000000000000000b0d51ec6f54400000000000000000000000000000000000000000000000971f9a84520aa31270000000000000000000000000000000000000000000000069a8682bd694d08990000000000000000000000000000000000000000000000004aac14ee1f1f9cc300000000000000000000000000000000000000000000000000023c403e139d5c000000000000000000000000000000000000000000000005840763058f90f862000000000000000000000000000000000000000000000001af162e6dd3d19c42000000000000000000000000000000000000000000000004606b05f7c092197c0000000000000000000000000000000000000000000000000002ec3674acffb5000000000000000000000000000000000000000000000000d53670d6246e537900000000000000000000000000000000000000000000000f962e8b1c78199d1300000000000000000000000000000000000000000000000a30b52d4914d59ed9000000000000000000000000000000000000000000000000000115258cf5acef2743e93d4e6374c24aef3bebcc2f4f2420e9ba7a5714ca96df52ec420a5812cc18aa6c4a565a8eae50ecf4b59ac47f274431bbfabc786534686220220083c6d426a4521a0e9e50e463baf8bba7d862eb979fee57112d10d6739cfbc5a6fd1f9512071ec987928cbd4fbaf9d86e277597b28f7f28b44afac1d78d050684ee7ab32ef47cc61e6570347351e7d14d514d6aaaa183d8f3136db330f81539a5ff03c021df9c271991b7567f82707a8ad58cc0f08a854d6c66f37ac04f4975ac8b16fa235b7da200d49e8945b4239de3bbf5257002bec0d02ca95619441e75985ca786043564090305898166319bc14b2ae8de7f23278e47910d4af9b22c1af6e4d6772bfd7385b9d88b321d75fb535fb79e9653cec5db75ad0ee5b430d3cf69f3f2e42a8c8cc6a439e517bcd51609fddca9af121f15696115028a34f01b99431f8d2d084c40dd7a68e2440be69d3c4cb4193013c9a52d743256ce32f3ae7ddb9966c82d4e413c9b46708028b32614768bf0ee52c354d61dd7ed7a6c07b4b2160877f005217f14563fcde0b9affa023d3ffc28acdf88fb52a0ab455edc3f200c41d78419495bf2c53688d2c38eb25f9b069fe0fac1156ab227d167346d68483944b5cd02d388139006df14da7dc59c66306bb63b141ffb44b153d3a2ce2be113a26fbd0f39043bbfa987f698498492d2dd5a9a107e44e8c797477789319d1c368f17d12e2ce36c2407c29941b527945fd15b8c65295e960b553088196c5c3efe8909610ac2ed3cfbfb0d8f6b42a55b7d96c1d6b876bb7eeaae3a52cb522e43842b6cf719af9dcd59ec0090750f2b640dfa3838c30b7fc60d524f21ec41e020f83136ab097d308477adc85664b412502e9e074c8ce86605f0bec32cdc0a92007bfacb4a0724afa887cb10b384a0f8a2f435e5a6a8a2476c1a43e794b5d040de6e17d96705a97dcc7110d434ef35baec043f75f16a8ad2a65ef85cbe11992a9cbb31614b1ff0f0f3ea28e6d81f4eb1d6f1370ba58e2534cb627378b8b69f6daacaace2fd158335faad367b8a5f88eacd5165afe29486713ca7d4d0a797d1b4a559d334dd13639843bb7819198343f00f242d17f223126fd1719b0817cf7fd5687b15fbd2031d179543cc51568d8451eb313243d02cab952c0817f78098276b027c46037f05df41d2b0c7bfb60da2364a6be4d4c6efc8a3bc3cfb91bb7800175e8a7049322469195f5e50994463f2ebd5690e6dd07daa9e797b5ef4cab2d13985f03f8994232deb337dd366766174a6024864c57858399fb5aa60ae7ffe0bc4f60558e5fc029d082fd155f55b3b7a1d5d11a9cc8d05db1bf23bf81781e0c95a310768386f136393e0465b8d7a920b5f46d99cfd947040ebf2edc4c9b56515fc197e7edb320d5ec04cff4e878dfda85a4d833aa29c3732cd973cb57044f72f28e220f086fb2394986b66cb4c5448a2e31fa30250ff6a017e4972a2178aaab76d92d14b59841bf47a8f314c8ed5cc73529ec625ee72879c6c70572cdfaa59a411146f53483803b645f74ae55e77770167dc9f8b9f88352d5d1405f2b2e1793f402ad14e0ea93040f6b5948c98694398711f39e7c5083d3a864d51f5d18ba87dc2604e5328931206b74e1af900f25a6159e24249b86e36c64edf56148d23e3e38eb6c415256d073fb0322559c292ab8be3d1bae23947cf6859296a20c33125166ea1e5db893200c783848aafa0b2076135f23aed7b2f6841c5d864e9ab0319b223b3df0dbff31c4ec54ef7cdce4533e6d6646188c6e79e0c62271353827e0cb821a320bedadf09e16bbcaeb45335a89d7dd4053c17ba82a9e0240b3fff9a7cd58af3f70f608d2e354669ea4f1ad977ad4ad13ababa66c1fc2794ee78c9fc4faaf890c9f2328018cf65cd2daff5027404b5baeed49e757db29c96fa3219ebae1a4a1c682c85131133f4d09ce60ca38a9cecc6a345a442cd11b6ed794e3bababd71f35c718c3b21a461b3893f154353b00e3e5c85832e6737f7100a9a5e29e421d31ff3fd1acfc177698f7e90ec5658b4e542ba9d4e202e46b6c576a1d694d7dc59c02236d052a098e5044c0f357415a36fce6e34465177fc0b914d8004ebb1ea7d7cfde7e91bd08cd0f8c745174106bea30b2acb002d58b77a984a681747c096470a038878b2619747a68514f726714f01143ba33c6067623d8d784e916716ff2da9c40f34787207b327f55b17bee72826ac64f91c43badf86e8117648079adc97e81059a5ed1283696ed822c0e5b0728ba6156a0636ad29ef805fd968b0efe04e72d174bc4dc05d38ab82149ca15c737f244b7c198deef75536eb116a6b70c99caf5a411978829028ab6b848af1a7bdf7240b46e78dfaa08f6e20a2ad9d72aefdb9b657184162d1ec631077462701554b4917f1598235d2fd2e728a6296e841f5109749dd4ac20add72426e1dd53e17d74de24f677e889c8dc52e462e9fb12989153424b619a168ef2fcbf4e23612d222a24d236a685e7ac1dee99e55a15ad5c5ccd1a90868c1f16d13b8af94831b9d998517f478b12843e79b2cf8b510e731df1e4e53d85f027ba9b775ee38e4722c1c50d840f864ad79d5b7f211bba5ace55624d9d67bf272750e7e77b265292e81e72b769ac39c69d6b87e0abf8a12905291bab45049bc9003580454d1f45cf3209f216a215f94e8fa0296039d960ca361184adaf05a5110ad6e799d393a4ec57ac1b496f0bd4c2d2d0a21704d2e807c02be4c0e65f71861ad74d91e1daae0ea9f7da1d529adb5387d14c79e9c88b2b0a5ea1354a2c451726d5453a94e0188988fcd6be18f9e0daaf1b3b4a4942c4ffe4bbe72a071ad2500f4019c931b7a61459490286f70ab69c358111ccb1062c984dca75e9be31d83f25a3f36ec88d2dfdba029caf2b502ac2d27ae6e2e4bc2c51310b577be3097b1904113b78cf1b719ea164112ef6f06d8203f3fa089bffa1864bfde3cb53dbc70d05f2887b68f9ab5de5336385f3970ac6601ca216e60bde4fb2727359a07121df2d366ccdfab360ef1f550cc116b3c4c12260c44d115662e16480f4653e71c601301100a4d931b185a9f7237ed93ffe377fb2c8a41ed83628be99e96edcf0ee871fc497dd70ba7658fb904f63552ce36737823d94cb2c86b5d043d00771798b0e137c6b475a99e2fa444f6249c5faded17fd2593e841741993be5e10a0ceb3fab13491b3cf515f2488e1ae936799cf3171105220015ed05efd0df77b02b685b850d4dc8e08ab1a1e4d84a89d55c2009f137b071b5ef47b817e8cfcfdfc318f7ee2e574cdd771653fd4ff74f23315ebec3e844c7fe91103cef4293ec6814eef68628463a2acefb7f863024e93710531118059348f66c509d87a173c331a959375d2d484ab1e6ff5834122e05bbbb8c5bcb5a3cc4f940e46e230bd2eecad42884042ae070003fdc0787eda93dbea86db1bc786f91b8124e8bcc48b53b91ad2743f316cdfb4f8af4e985375743c1ce93ddcf33243d2a785af13054b6f0a8e2d7bdce2c0f57dc1c3c616fa0cb441100f067dfdad8a704b9409c752b6a44a8192af35104d6a676ac93f8ab16780b7311c4d9e09ee52618cf47f75ddb4debfa560e325f13720280111945493a97974e573a977c4be5cd28073fc26df9e47a030a7b3cd60a9d160bbdbcf86a754657011e6221e8d3c0df4cc4f51233d4a587362f34660c209d0ce770afb06493a48ff0188e7b0f2bfdc3290970cfc07ab046a894581ca208af11ceaab5973914f6936e5e1bb5c7171e41a83758ddace922067cbda7bec528a70f53d8652e346f2811637a2bc348fa4a329543ee215284507e18213680c01b87d158aa750a942593d21665b070248eb05d9d99a3e718e83d051b80b4d0572cd3caacf4b04d9b02e7b5d119f825c51ff746f1b90690fcfff820812513341a1e518c70665d56c4fdd8026696ce962b244efd18337beb6d851371691ae548b0134e21cdd57a574975f136c4c9df4fd47242e4e8d5b4d4d5c64d434b7dc224f80ece578109ba28c79b93d58ca355122180703b48e1e65bffa0e4b8cd0834892a1af3698b86a6f19f95d7bb233b3da96574a99ae4be95be5c6a1ac80cecbaf4100cad09b438114a5de5e688f2350b6bb9628a8d529f64a0d03495a940b8af841219cbf4d848305edba7019532ed0abd817e4351dbf89985edd837bb84a253897f0f45cd9cb9704c95846fca3cbe168336a7033b9e5484e1ffae1377e5bc24ac8604d341ff988a69336a5227edf5f1ebfe4c03b8080006f1734bcedbd13c88998b04fd88933744df41cea43cf4364514c189bccdae52b745765d39cc7323e0808926e73ff470ae40e1274dfd6e4f1a1e43d34ba15f6cf124dd408631b349b3288d1bde5a64444aa6d4d969b48fb876fdab78c00be233cc6d7c2285b5d57e0d541a215591b9ba63f7a1031540a0fd2ef4b25c45401435283b8f5714a6593377fef92883a753b19056b11835d43a8dab798ae41ae19bddcc9b77e4a7d62a9044237d25debfe3113d1c32ceef3f28f255fcd7d7c1cc926ef05eb8fd38452db84907580238852d6123effb342ca4edc2e2e6cb1f3503ce645286483a24fa0d5a5203cc1901f6bf1750b0562f89622e7844fe4dc4df62f393e2df9ffc367d5559fa0eaa2e9d24c26cd530248c3612b7b92324fd60e9f2809a7714a432795348dc35e1b6157cf914b2e55748cff5db38d153c4bbfbbea5d69640d6ee322b2486d2ed27b206d734392617d64c10f7e920f8b861f2e2ff047f95f64234ab271cae6257fe270956d1443a7ffa4ed7934245c40547d4f1849536d1a325f89ab9c7068a918795136bc9fc6e672be03de71c3480bbbd5d86b9bf3271aea5ac41af91b48c52898c165691c3be42b9f43326f98e9240862a025bc5635dc1c606fc0f2509d1efef1b0fc08de20191f99e02b70875d3dab5664655fdd5290c12d141284f03b5760975043a240b864666ca80bd0d9834ba077440690d7e32958bb09128d192c4a882d80291a7bdc10b03ac9e1ba37e18351704ce1a60bc4cbdfe5396fe6973993ba8a0217669bd2f4ff5f684fdf64f00bd579194d190e52d3404eb217b7bdb07635a8704e5e9cc2eedfb4d5eac2c8c97f99a67a70b722e722545ae14a382fe970a42ce2fa136aa4acc0f44847caaec9e9b5894fce40b03f53e879fce904b96b2944c3219a345407d3b0b4701deee680e0407a6ba609db5af0239234e3038e2abe9573d1cddb19fa9e9f7cf586724b8ed082e215fdd0da169f6bfdee0c722e449c65f262b2aec80e8ee480b6410af0902bad7c53ee032e7835fe9dd4d3be4567195df882e777b9560db7442ad719eaac80f50ef874f2c54e279f3d54624ee02f47fa9501e264fe1f239017e8a52e0b02360126fcd71fcf44f9aef62d0eb47d6a2ae41e72ccdee6ac3d32aea7fd40afc96b019f5a9951a0b411e648fd40a41c1da06214401b6bd9f4b96db40cd2e3c3999220a789265f0d09426777713470380bc5bfc5e1fbbed783e3fec98db96fbd0ba69cd290c27907ef7b1982e7ef331bad447a1880722f5b7cb793fd8f27b9ed1d31dcae36e2338a2131a928c1d0e273db270b05e2f9b8b2e7e2762b41e67be8ce9aff152d20108397f3b73bcffa359b7f1ef0d4a2e963a4898c66d5706967babac451f8be3c3576e914f059af6ec9aa0952e5753019bd4cf18e64290f1c165856c17f0ba9651543a42a2ba95777c40f383d5d5311e747e15b6bbc7b2270975e0fa26e5a46291cc68074be35e4aff55b3e5ab75ea0b86348e389c8c5fd94f675becb39304de9b7aba1f5f24f9436542ac7270e91809379260ca1721fe623ed93c31cfc73290184e3fd5be777ff9748a62635aafce2400b92b1237ecf7292a17c5ac3b3d49ce2c996f9be7c74022ae9328269095f4261d23c6956f89f4d53b4dcab088be3fd495b11761c7738db37914e27d4ae9a02f40c24808c10c618690498fca7f59fb1d4ad4900940178b7cfd3c73d8a0b4a6299532a13a9c4aafe6663eedc2abb30f5dc5efd034730725391abd53c9b10d0c2e83c607da7e98c97acb9a1f579133f4cab156b61d5db4d14bdbeff7f42c65d419deb1cf3760699ead370c8b82510c0b32c579ed97d96a225c1ab0686fc6130a074eca7416092782b3271155c927f47020e8c0f1323f683cca2ed574050f733001814f79d7e5adab32768514f5804dad018d2ffc2eb9339ce639e4217b7cbd59290c6c55c4161c0fc95458c7b2caab39bd0ae622b1cd31a6facb5771fa91fdc61b8350d295d4a8ac29612ee0c7c58393f0fce2304cdd04e26b903b989a890d2512f3245c84b74f70e541548f1b43103ff12d4183ca15f6c5c4953149159f688a066190fb34a540cf37b4b011e5d56d702be708ac52a6381e6c7c14fd5e2390bf0f02054dd47b7c3130ba3b05a8358213791d14abd0942f197ff41acfd49e25000029febbf849756d5b0a1ea245c8e72a8ff5d8142e0dfbcb03d081a1e6aa523c0b9be309992df10251df3500bbefde5e398900b31a2873400259434818825128009b76de86ffbe9a22f6b0401cf2a1d31b9bc7a467edc49d08bad4a3be6a867e0369d0df4228f24157e3e74bbf98cc4fccb350cc0b1453985f35bd6d59eb3bbb0c8a708fab3e36564c0fbcf59b52b07d420c6c27b21dcbb404d78ea17ab65ab22a7ee682d495e473c0b1901da9986e35a66d58b90181b66c648ae8a057a0c65509d48727dae811979c6a30df9bb90dbb3835baf0439724bd6a16546f888067321026e81944747f1c22e9ae204cd1991984a23fcfd5310572b72d74248c91be8f1d39a8b44143e8e86c599d26faad8bdf1aaa2dfe0a5a6bbd420fb8cc4827abf30fe2a9d0839b22588fd2a0dc6cfb92fd6f86afd7f9058f6910f99e65a315a0c106958952ea6ca6135d6b09f6e67571bf4a4e4641f96965dd1706c572337152531aa504225fd8ef9d54705af04553d536d3dcfee38b3de834864252be74304ea8088fafea00475b0ad5d40f8ceab5961c9d626cafc84b26f84400357447687096293abc23a2e828c5c37c97c38f866911d9ca984a03bfa86b291bda99435500732ecc4388049288b6b777af9d4758126f2a552bcc16e75bf97bf596c0c497237f1c9f0b1ae3d97bc4531060d3ac576bc01cd9d5f10e137030e4bd33a81e16a8221e79ae12a6f7971c03de925cc87490f9b64ee9db4168f1b03d18f286ee8efbed28f9b5457b522f7446cdf3390c66ff86bc466884af706dec76517f9be11b1c4a24ad702d86399352c2f7ad293ae42d8eed275aa8ffb377b3c4f2d547deb234e62763ff0f3e58cbfdc39452771c14cbc6c8eb0e6911d53ceb304a20947766d8b415fdcd1099e3d5ae5021bc943de2cdc11ab57fa9b5a6f6b063ce886aa3eafc5d1a518044009fd94014486fc043c811bf0ef250d0ce1a073653c935db8219087c2840edcf89f08a32eef607cab2cb9140e18cc0cdcffcb023e0b26ac6691e290e01def66e7b0a31bb1110291cb92cbecbb0a5641fb9f175671ed9a114b757acc6169bebf9c9bef1c27372d5509192f4dbc0999b57d6501f2f4e04bf5be97bfb33138ccf7f178db34be5d750cd2bfd8a2375ee0ac34b4410896ad8875efb8d81131f2d5c591e39c8adab09adb775f559b695f390e934af08c8182757cf340407450bf3ab02aee67a410cff649e08fb935e1f6ef6556706d6f02464125ca1c6eb8a2db6877fb4822ab722f3b8e9bf03be6078fd699fb51125971b53a39616de119605075a795d83199b15c0d103f0c91f11395a517d1c83340c2c91aa42663d1a3e071e4414d800496e2ee7d8551acb3426677077be3c35d7f41c69d4bba4606ef7036ae83ef1b1c06905b50b2fa351b94d4be937016f6d8533368481481ca6fda30304474257de875ef5168fc4565468471c81b4c65719d4037d7cc7c835ccd80c01de4d6a992f902e74fe1b3195f1ddb53a672b331133175443811b957c1f68090dc846cc9b717234f1aae65a03dc471461efcf21e69d8be21937dca5aca848c90b98782989e7ad92e85b4f3ae502fed8f71ae2286d1336ac0d32b8a33de55273139cc206d8ffdbfba5df914eba7251435de570b239374f98276811d3469e45771db82c1754c29ac6c6e39643b34feda8dc704facf77b1eff5ef2fb8e7aa71def11a5401416d16223009f6de585be285013d644bb03ee8d9e124ff9ecd139914a2d37d47a97ef260781595c5a33bc089bf5b870b31c2cbb6e2cb96551d08fe1f100f2311a43fcca3ae43aff407034fb1b3ebd7b2a26127be42820fa97cfdb8fa02f41c7e12202331d288ff905e07293cde5fb1b4416853bb5c52bcc2120a445072554185db5bbc6c13c023e10e818333b3ea63907bdcf0275688f4480a8727b2f183dbf9242fb2dbd3320d2196863f6fb29fda90d4d090bb3afba4a59b2c68a5516cde31823fcc0d15ffd5476a12e54eb6c38b28d04a9fc2bb41e13ad2f1f2875138bed8a04299e7c714f7413e5f053e37e304248e7d969a4f994d15410f15c2b1d2aa31f2d70042e1d242c15c593a7a846a95fc96ba029425aea988212bf900e0e9b2a97f5f8b3549fed8148d271db03cf878b601a6e694024aefcc8ed72093f03a135b5fc9b72df85fab4825c6352711b7a71fc9876d66ffa5949500a6508c52518966b85d42ab90240e68488817e62085f222850ee38137f65ef5173eb9d341f47eba397a1ba71a4509aaa73f535b5e2a771a99a080ac81c2632a3096b287623a2779126dafcc6577a643972a6922fd3eb24bd3281dfb6dc1d19cd224a269f013a2861c8b8fc8036d89c1689e27a68ff32162685f45ee516f55279ed6b32372a3ca326977ec6b34cfb295fbc099ed807c2284bf81a78546681214ab14c4edf113efb51ddbbb7189ce15790f96f54615db145c0ce9e5e818a59e0ca7130a6921b9ae2514327086240dbee82447f852fbd5e599af80db39784af850b88d9060026e9833fa6c872b2c1372f93099606837851a1a168c48145d9951640a97b72092042dee789eaeec2724b826000d39dbf75ba4710212bfa31068bec37ddc16a64050095288cf1d25c9f9489012d3349074eec75085803491ebd8298924143273d1c498547e97386e641295ec8431522d17cb9dfbd2f2b4217cd83c808c7c5f18d10737bcd0f8950e8b2555b78e3f78e91834a37af5386fdae725e3f20bb54030e189b7db9b6e8ec5eff63867cba626e652be6c46d665db454445f0f83f08084af2113e5da93928627ef87936d16a7000a0a094a5002c5735caed7f84e96fc8af1114de25f98cadbda11d3a91d4d182424c842fddb7c69cf66ddf1ecf33311c9e91b198867975fea5bdccc98d7e30e8f3c8d8a12e2cf1da8a91da8373491c658ca0ac6e1bca3d1a63e41cbcb27336f3072f08b53d48c7ab0d0e077e2aaaf1ecc0e1c3898c8633a490bbed7d87f0b648dc8f0f18c049f79f8510814b0513f9716542796fad753e5425f502639f66f85280d9a026609045e5ce3d85d58cddfa0760f2da4146c8424d471d5bd5992a8fbc67b3bc24a107b83575ea4ec667c8684d4a50d879310cf292b4176fd2da386d2827c6a949c75f45c1af3841c6742811f4e532b67908e68172683560e3ba9deff96afa53543c7525ab1e2572c675585600a3c0e8d9cccbdf1d2dba272332cfbf5dcdbf4598ca883d526f0348b407a0b53b7f72d63519b8a3fd6b14bf1bbe4d0caea0eb71f6303aeaf5198f0b96c540561c7b024593b5c3bc10bdef4eeb418bd7d40e811b46a10d85eb1d03201b1174418ebef2f8d48c74be847aaad4d92bb1c1a4905fb0fad9118f778ded6e9f18e291e974118d6dfb276396599d87ac39e61fbbd9acd173234149be49b533d3329a6a471a00d06013725130cab4546ae6063008eb76f952a8f7b1d4bc364948346145a1d512950eb1ba9ddff18f9a5a6b46dd0375ce99fa87e59b41ca52fa1956a5f5b54851cada72169d4dce1449ab2efb0c6b9ac9e152462a4b94938d0e02d656b2b302516047fd71cfbc6fc5f71c30e004e4e77fab46d560bb462bdb52508506099fbda2a132413027e013ca6de54dde3497cd6f4e165855c4daf55a23da099c2ab11422b7037249598689857c3181330d7c7c0c85723bebf2fd38ab79ff15ab71c736e070cce892a0e995f3eaeeafa42934a50cd1c3f68cf168c50c2541e7722e08b5a1952dc2def02fc665ba4ea42f9820ec05e6f622ac1445b710685b911d67280240ab866edeab9a93f511c74e06d9409f43ea822de953e04b405e4e258f3cb641e1d51e5848cf19ba0090c881d58acc9d09c8122626a807299cd251dea77bfdc6c1db5a608d1d7f90441ac3da7a850b39d37ba055d46822b7f4a440b369ee3dbfe0e2e4afb5a995b21b025423a3421ea55f22a69812efe31e835d00ffbaa26115c1e6fcc35e3bc1ecaa89b717d2acd42f0ace83fb1e88c08230658c64331a0d3c002205b52ce6a9a3afcfdb7fd07f0ff917b0682a827e6be0e519de443bd7855c028ec2309d0e2f5f228b984c5874b5c1ad10d7bd97a10bac7b9828e46ed4b5a281205f36b20d02c74c43cb5a415a3785ca8bfb4d3d4d2ec5816901120511ac4f82ddf777d020409db617ffc7d064710afe1af7171ab23eb3a2827242a81ac15a00c068ef21b34310cc14da591ad18e6e044f3eaace0ee07bd05ecbf722a7b6c292bc6f2d578c6d7d9c998f997dfc03a913dde3e1d965b17f32593a5a0627e0c092c10fda42bc30f3ce904e4c26eff37953802d60afce5cbb470bb6d15412e268f3023a4042bc161e38fd8e621f9d459f3effe4110dc22da5fa1868bd3918cf7930d18599d713f0b6c205283389b0f2453340975f771defb2b5e31c3857dc90fd92daf36046a3cb64afcd9050f4bbb9fa89428624a066a44dc8693c560b09ba22b0a418603118fbf9d862084533831534afd2aaec3cb75aaaf22b396d7188bfb0e0b3709fb4ce0a08b7dd002572a13ec48a5a480dbaee3570d1fce629dc5d0e56b21cffbd42d18cd9b62dd78c23ffc8d2f5ae1c467b80afb45c687c9dc954e8f321b43ddb100a18f3af3880a51969626dc3e15ba3bc4980e171184ee3bd89dee052639becb1659d8c12e42e0652b6a33d148999ce6153d4f4e7afc0e640bb231ca2a59dd79ce91efcc51caf5495bf45e00df2beb4120e7d30d2f5a0446206fb6e3196ba3c835908bd1a2bc0754ab6bc4819f2f5210fa494f76792070e5e2c454be02b1da704e87aa8489ddb9340f8841d94446eb311f21bd78713ae2498aaae1631834d8fede4f068b2a340d75f9e37b796a0d014d265179ae4f24c0e79811981d2ef1b18f66869e914aef1780d5bd351e58eee8423b18cc9314079f6aa2f344601c8b4a05c7631d77da06ced0416667ba392875d9af73a3157ef63673783dc13826a144f350975fa8223473bce2d7337890bd3cf3e56eccdaa2928923b06eb9f5178761d6ee4c5c09d2c2970c06cff0a9baab2d0718f5db3f61455817fe60a7862f8573382c9e2d1eb09bdd03bb2af0db6344c045fb86e12579a8205b460b1c0312cf88883ca725d6b7319535c10442a31afbb47781e7b486a30e4c35a016770c23790213fc854527c7dba54a75a9de6b88d741433c32774d547ef5e9d86d1d76125a9c41bfe6c1c5414b4954c5ddd09c8830eb22edfa6888f76e6f227358ce321c8233821ebcf0fd330a038028d95c8555be7bfe7e30a6e9a95934ca77461df027d3e0d2c5716339668a42412bbbc5e10dd51fe22a7ab706d88361503c99d4ef0b15a392a838b5c3b67c251972cd268b60c15d3fbbd6bebd225ee236d219d942076f9d9bbfb77981cbce154b5b9b55a15e74416b9af9b1dccc957bafc744c124277d650e13f2839ee6faa36b0a3b4db8597de30e4b95efba2d6aae287df1c67b1eac34ac25df0fa2360d47b727bf22d4cff0745872825394be099fe22b57a12614ed7350f7e16da6c81c07dfdec5c33efe7b6486e06b96a4659ec6fd8671f6a82c55903d7c139842b424d91666571c68928cea428bc1d612d6e7f44bf7c27d0f17ed583db00056b3382cf91233026547ebe5f93f1aad6309cfc2e419a7c179520e99025f7f20d9951f7b11b0319944d7d18c1636bcfea5f034be45a58632372204770cb1ac257d09f1a6a07837916260885ea279463d727b331b264b1f0b49381f7f2e77f8cc6a69171a6a7ad635e415ca81bf32489b92644e873a4fc170ffe1117c1dfb8f1d10c543a1d9047564d769469816930e3f41854864298374980cde254352efbe0abd397db0fd78222d71595961d9981c996d5ef96383dbbb766b840876fa3e4dbfee7486a0199e40a93774fcc202ca7e094b1bb99bb5c03d5128a626564e425bac7f3eac2991821756e078e406f16d5fa573f59897d0d345244d590b3d34d64df3afc81bacfd30b9e8164ed6e043ec5250231e37f9a6a8f652bd7f0fbaf17b908d410d6d947d7fca1d3b51ace99f755928d9aeea46034d972a296d25beb92b01ebe662acc722dc0eb9b8ad40227e67acc989bcc820773e1c83aa612905aefce55491f1925af1c04f0f231be3514324a179e5315a1bb37dae95a5661f956272bb735d7b95e89c33e7c01be2b7e83582ae1a355e9df4ecc315e84dc62ab2c1206457dc7ce8ec99f837075700053651b11109c8627bbc53705b593bd91c3da4df434d327d486cd25a916b8607d33f31ec6b9e9bff15eed344c0fe88280d877a5b4bcdb00799f7837455cd919fddd991bfdd02dda98cfc21db8b9b880e0fe664cd7591257dcc7cc3e0c131721b88d6fc684e93073ba5d5b050eba773ee1f1917ab154ba311a46e1de3a8818934b0984ceb63d6f8a0e8fdd1e88f8ccb8521fe6811cbc580e23c1ffea1b329d7e51865a34d1c0fb717e870a221cdf9390a1815630d082b4327aaaa31d4e2c343fe24db2ee45cfb6209e7b3f052f65ea94a05769a31ac312b65e8b702576524d2e51da83c3511a1b211e67cf750a2e2a63a003f5e68ab24af990d658fa911e9b16d48e2dd4c39a5420208eab00611a2952c14a3adf3a46fd71d7dff35d4a329ae6edf88bf82ce63767a264c0f55f58152e12f9215f4f60bcf23a87a052d4ff98d4ea15397227ef1a31e27630c2598afeb781697c705ff91abd6ef567672b21af372848a5b907b137380b6f35578fa10c0442f8e8a9c1185d5390101051a7bcb9a9cc26e54dfc06dce51a827d771e712c4e726a462eb20b0b67b110f61c511ac1d1768c17bb76bd94f4687e926e91d8d326a169341d5a6c63d0f4d2a4657014e2d32ef9e2605e83ffff451efd4e44c31e4331cd7e76c07fe587393bd2896c20f496a1284126981e6dd39c9a93489dea6aad722589bcf9152050f6fc89e1c9bdbd1cd00b14c92dac4b0888dce9deb64d34b6e2799104440d4688bdbdf16aa80a0032eae6ecf4f5457dc7ec502f9d6541e57f11d0a33df49cb75b224d68297662a24fcad6c6c4a1d058b66b52e5194be351854214297bd50145c5117c2046294d80ab362f32cefafe752aebb35e18b6343202b088a5e3740209e74a2dfe097771d91abbf91821bd6d8d2775370bfe80c3b05a20b28e7e4a875a7f7fcfb34543326cca4cf25a9711579795fe1cc240533bb43bc25531a47002615f2a582ae9b97af5ab449ee49ebf1524fc70284c5db9d9ab8d60cd6a756b5b14120ec32650a922f09b9ff0e05191d9cb64e8a49bc9ba9ff38862f8438c9f72d8de501a998b72b3e094cee71964c093774e6cde95231bc7488af29ef82fc95f20b73e22cf7397dc2f815c98cc0ab35ce3aa2573b3bfa52c1c4e20b14ce9085edd1ba3efd5b4e7eae79db51e0c0b6eccc2d8c2fe017b1e953050b10865d5f69252f27099e8607385efc6b34356105e0d55711c256623bf827cd6601ebdd0093ccf8b5b7c22a4a7c023a8f3f6f9c2e033af6e983488fe17ef544b70afbb9a6c7baf14c4c66d617464ae680959258f3d6d0921d8220bfcdf54a201307980f3e5d8997783a5cc6260d02bef3906564b64e944d925cb366b68aeae3762bfb562400014dcb231e337e4cb1835d935a6ceef1cb72c2bf7ac05b9b7063b82b65f37d1d1f7836b8339d8b2588c630d786c8a0cf090161b1d37814363803581fa5ce82abb747da6d6c34a0260e627bebd1d4776e9e328fafb6d1383fb9b2062ee54c659b246a40a81b5eb1901216be06a9a6da8aa2fcba770d5ce232f8bb551259bb0dcab25477febc5d2db3124082c3fa025144e7ff3a2f56d6ba87a7d2df10fe1067d51a1356857e5a675b99f2573747dc685678c1896d6d820d5fbb6a142365320332eba438ef44aa656caa7815f18b3f9c59eb23e8b60deff463beccff1f48996cee11a6a538526d034a9b60f065fe0f5039bf073640b2ec41db6fbac5193ea88028976c924952c5225f55aa1affac5e7586245ba59dd8a2df0bc13af700e67b6fd740d52b6bec53ad840a94f31169d6dadbe51fc56a45b3fb95b7363e06027ec79d84a0751501e4ce5afbfd2be1117af68a171593a2ae7f2517eeaf3d0a26ef542845f9179278f756a8155454f79686846fc0527bbd8cd9f42ef1287e06eb851df7c68cb72bc28b11fb26f5a0302f4d1431d9bfde843ef7c022000d9322e65464fc3fd94083d6b1d693fc9bcd311e078cc6fb4bf837c86b1336509b0c03785ca3629140210823f3a34e477e6b53113a069203acc49a6df56466be294f", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000204570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a701de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } + }, + "test_exit_code": { + "crisp": 0, + "folded_export": 0, + "enclave_contracts": 0 + } +} diff --git a/circuits/benchmarks/results_insecure_agg/integration_summary.json b/circuits/benchmarks/results_insecure_agg/integration_summary.json new file mode 100644 index 000000000..9de4f9ef0 --- /dev/null +++ b/circuits/benchmarks/results_insecure_agg/integration_summary.json @@ -0,0 +1,244 @@ +{ + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "bfv_preset": "InsecureThreshold512", + "lambda": 2, + "proof_aggregation_enabled": true, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": true, + "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "multithread": { + "rayon_threads": 13, + "max_simultaneous_rayon_tasks": 13, + "cores_available": 14 + }, + "operation_timings": [ + { + "name": "CalculateDecryptionKey", + "avg_seconds": 0.124654569, + "runs": 3, + "total_seconds": 0.373963707 + }, + { + "name": "CalculateDecryptionShare", + "avg_seconds": 0.625217639, + "runs": 3, + "total_seconds": 1.875652917 + }, + { + "name": "CalculateThresholdDecryption", + "avg_seconds": 0.556127167, + "runs": 1, + "total_seconds": 0.556127167 + }, + { + "name": "GenEsiSss", + "avg_seconds": 0.166924514, + "runs": 3, + "total_seconds": 0.500773542 + }, + { + "name": "GenPkShareAndSkSss", + "avg_seconds": 0.241305361, + "runs": 3, + "total_seconds": 0.723916083 + }, + { + "name": "NodeDkgFold/c2ab_fold", + "avg_seconds": 7.62245093, + "runs": 3, + "total_seconds": 22.867352791 + }, + { + "name": "NodeDkgFold/c3a_fold", + "avg_seconds": 35.477445166, + "runs": 3, + "total_seconds": 106.432335499 + }, + { + "name": "NodeDkgFold/c3ab_fold", + "avg_seconds": 7.549333777, + "runs": 3, + "total_seconds": 22.648001333 + }, + { + "name": "NodeDkgFold/c3b_fold", + "avg_seconds": 35.646490666, + "runs": 3, + "total_seconds": 106.939472 + }, + { + "name": "NodeDkgFold/c4ab_fold", + "avg_seconds": 7.196253763, + "runs": 3, + "total_seconds": 21.588761291 + }, + { + "name": "NodeDkgFold/node_fold", + "avg_seconds": 17.672638027, + "runs": 3, + "total_seconds": 53.017914083 + }, + { + "name": "ZkDecryptedSharesAggregation", + "avg_seconds": 8.409093792, + "runs": 1, + "total_seconds": 8.409093792 + }, + { + "name": "ZkDecryptionAggregation", + "avg_seconds": 49.418440542, + "runs": 1, + "total_seconds": 49.418440542 + }, + { + "name": "ZkDkgAggregation", + "avg_seconds": 20.366319709, + "runs": 1, + "total_seconds": 20.366319709 + }, + { + "name": "ZkDkgShareDecryption", + "avg_seconds": 3.170409645, + "runs": 6, + "total_seconds": 19.022457874 + }, + { + "name": "ZkNodeDkgFold", + "avg_seconds": 111.173713722, + "runs": 3, + "total_seconds": 333.521141167 + }, + { + "name": "ZkPkAggregation", + "avg_seconds": 4.325023125, + "runs": 1, + "total_seconds": 4.325023125 + }, + { + "name": "ZkPkBfv", + "avg_seconds": 0.433745833, + "runs": 3, + "total_seconds": 1.3012375 + }, + { + "name": "ZkPkGeneration", + "avg_seconds": 5.172915375, + "runs": 3, + "total_seconds": 15.518746125 + }, + { + "name": "ZkShareComputation", + "avg_seconds": 7.412439382, + "runs": 6, + "total_seconds": 44.474636292 + }, + { + "name": "ZkShareEncryption", + "avg_seconds": 7.468529083, + "runs": 24, + "total_seconds": 179.244698 + }, + { + "name": "ZkThresholdShareDecryption", + "avg_seconds": 7.677126833, + "runs": 3, + "total_seconds": 23.031380501 + }, + { + "name": "ZkVerifyShareDecryptionProofs", + "avg_seconds": 0.114853486, + "runs": 3, + "total_seconds": 0.344560458 + }, + { + "name": "ZkVerifyShareProofs", + "avg_seconds": 0.24184635, + "runs": 5, + "total_seconds": 1.20923175 + } + ], + "operation_timings_total_seconds": 1037.711237248, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { + "label": "Starting trbfv actor test", + "seconds": 0e-9, + "metric": "wall_clock" + }, + { + "label": "Setup completed", + "seconds": 3.042068, + "metric": "wall_clock" + }, + { + "label": "Committee Setup Completed", + "seconds": 20.254953375, + "metric": "wall_clock" + }, + { + "label": "Committee Finalization Complete", + "seconds": 0.005661959, + "metric": "wall_clock" + }, + { + "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + "seconds": 131.583918, + "metric": "wall_clock" + }, + { + "label": "ThresholdShares -> PublicKeyAggregated", + "seconds": 159.570454958, + "metric": "wall_clock" + }, + { + "label": "E3Request -> PublicKeyAggregated", + "seconds": 162.126205083, + "metric": "wall_clock" + }, + { + "label": "Application CT Gen", + "seconds": 0.3074025, + "metric": "wall_clock" + }, + { + "label": "Running FHE Application", + "seconds": 0.003480958, + "metric": "wall_clock" + }, + { + "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + "seconds": 57.874201, + "metric": "wall_clock" + }, + { + "label": "Ciphertext published -> PlaintextAggregated", + "seconds": 68.103220875, + "metric": "wall_clock" + }, + { + "label": "Entire Test", + "seconds": 253.849896667, + "metric": "wall_clock" + } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000ca0bfc9448e9ef16c000000000000000000000000000000000000000000000002c6fc73ad221ed124000000000000000000000000000000000000000000000008224c7cdccbb18af40000000000000000000000000000000000000000000000000001877d7b0fe868000000000000000000000000000000000000000000000004ffaa9844e438d4d300000000000000000000000000000000000000000000000ac1d3924637ac840700000000000000000000000000000000000000000000000b23147b92159485ad0000000000000000000000000000000000000000000000000002e033f6bc040300000000000000000000000000000000000000000000000768068eda5eb7ec45000000000000000000000000000000000000000000000006b320ac7697a7d933000000000000000000000000000000000000000000000003ca3c43312f4a1a730000000000000000000000000000000000000000000000000000b90e9ebd4813000000000000000000000000000000000000000000000002d7efb8bc1720f5e4000000000000000000000000000000000000000000000003f44e6bbb5f7dd5e000000000000000000000000000000000000000000000000d73b5021fc0617d170000000000000000000000000000000000000000000000000002138f4eea500814ddc04dff7aade6906eaa66607c4f4fc4b7fad8d7d6dc1781b1ccb5824ae1460e35805f5cf672d03d0c6e86292f9270e308666e0a68e54ba34c48c7d1c1171e08ab78f3b3a380edceaf2be1332bb0f6970b24f889216fb52b96903c3672078e2a2d8c18c4b84f1ee541265621917acd4fce05eec99dc817ef9a1b30df545bdf2ffe8839de28e724ff17364a02a9190034ee4093d0771fe513866af5eee3e16c2640fd2eed50030bcc39cdfc2089b3af2b2fc2cf47c558c544d264cb316924fb06cabc1c64e480876c03a96dd26e2ec80e2a1fe4a060eba855017ac571e27bf226f2d314aa75307227826f8c8cc3558237c0c85427b4053190b5a66b8518499a04c8896e47bde11d94d474f7eb7e24a697ff050a323e51605c0a2f3795b43a55216ecd8b7dbdd337ec0eed71fac7dca88403ce3aad06ca8e1f5804dbf2dbfeea2d01dd0ea69cbd98b44cd4163166c47696b0cb64815f84169ca91ff184bc2aa703873167206ba8215672551c6c39711972e0fcb6171286004706f3f61d7c56d50f286c3eb1632be8786c44dc777090a9f3b2528a91259674a958b8287d9e6e7902eda561c95e35960e5fc1a918c47214a8a3fd83999188e9cf8c4bb3af86a867072be6dc0e4cf33e535bb85259a027f564bfcb50f2633630222848a6fbc30bc81f15de7cce451611df563628ebf63c866506617a24c47ee9943936bedf6006d403e517ea950c046b99c76b6a2f049de41634d85d23ec7fe432a42c24131a81e41e87bc342ff0f4a94a49a0e42bc24fab0ad8569dbe45de50452d2f1518f6964d106282a56209e9549419539fa23a7a21d5adb558af735bb87d547053b4b077b328e1943bbf60c5f59f31a56dbd1bf144efa369f33bb0c50f7e45ebc416e850d2241b4c910649052f98c615c093e763eb742f1cd429f36c4824b3f875f18c45581825b2a7c498228972f28cc96b5c90ee1c1dbbbc46d9ffae4973bccf711fca6d17a07d52ec5c6aed0fc3e9a064c37ade09de5dcae4e637f46271f943e8933c50061892e9ad08e8bff03cd7eddfc9ffada674bb27d9867e628d90ef6e9fd19ee92222e6a3072e0948ff4d178389a49a145e68efdbf306e35874e3ee27452ff17028fbcba83976dfd49e75c8bad82fb9339e77aa7fb76af7670b90e084210ebcc223d988bb703c4a113f7131e52a583d944668b72c08fcc4f552a6927dc3c7333b06b41b4b298fb5caa9170db380fe81195d0da8c38d6de7c337c52e5f024f046526d3b8bf4befc00cc172fde6e835ebe761c7af500c1a3de202df707e91201f7a02d38fa8b9456f6777eb006c1834afae72b3e500ffcea21a36b82e83a704da1e1863aaf8b58a8662f0dcb6fe6fd3e35bcb4bb922291315bcdd7d2f00ccc7db3d20e4d6112826184c37f83fef8bbe495aa3976e2e8c334b967ce4fc0a94d26503189d92a2f7b89fea1bcef0c5ae1147d833e4ac1d52120b36cb7d10619527140b27a521e78a3d4075e127c21300306256edc76d094502551553efcead0caec3b1097621f0a820ca04263bd57e7afa49cfb92a50eeffe2ac26f7502a496adfdf8e23d183479522e4fd838ee09a8fd50fec4a22103536fb118a251999b16042603d025ddd4670df746903205ec04aaa1c131a8565298996d2ef8ae601c995ee0e1c27b2ee13e4555206067c20ee5e054c793933fad112f9cfb4edefa8b6a5cccc8a01fdf4cb41e6d9bf4216246445428daa66e8263a65b79139ab1011ef9a150b9b1a40b8798c1e4e008ec02795bbf95bdd7a531a94ae8df852e92b98e7766fa3ad0c79d387b21791f763ca553d7d225632bcf58e60c6c608ee1443f98f979f9d3603afa7aef8fb5c103ca78c01bcc6917d4f1e0aa09bf3ea417c51c1c3f9ea2b902151931b21c9d25e574019fb092e03c40399b0b8fad6e3347f5961674c366450049609cb8eae6ff4e987806a36851ec132675dfdd390f61fc185b808b31af6ab220359a2710339887cdb032a26cee0dc574e33c1651f7e8b746d016f39fff2c605cb5e7f0b7a839277f904d2bf506ecc02f74be5baa88793d3265700bf8801312174144bf5372435bebb6c3af3d31bd15519858190765d99eb03caa1b516aa3a219db366834e6cf2d8dd47bafc50fce17f23a9556dc30699f727f09edecee94d252a7c4d5498c27a3915b7ac677625ac6829b5777a2dd234491d87168bccbf5301b9221ff82f1648b3b24d2e107329c0a910034296426f76ccc9941c1267df6d14d180edf7ff58f8aafc9fddc2b6a9a641e608c5d6ee03ccd615b645b17775502ac1b479e0010f878c96dcfb913639debcc8dc163faa63994d6fe4299db056f125cbabf7fd62bc792e6505b14e5849ef4506bb43babd4cd46e9df6d402c7e4de2e213bf9ca80aaca06b1baeb5554cce579df7cfad676200cfd2518592c92c6810728dcbf5a308018bdd6a9d0d6a3249cd59a8725da15de0a96a899f510c5407711b0753955e733c32aa3b780c292dcc4a8a24ff2f7bb224370ad0b0c6fca1f601947e99c3cbb54eefe69676eb12aaa3473410966221a4980511010e6f3212c281aae2b27f42ea11fb627fa4c854828ae3d1bb27551fb5ec75b54e342f5cef48f0d592aca51491f88b890e7dd0aaeeb290d165a763aa3b7f3b34ff65fa5a05f8f014f6b3daab5bcefdc03d0aa1f38464f5b0791d8e1f379669b72f548d9afe3490cbeeb0626f19b784265812c1f6161978d27f9e9c0349f028fe3b71197ef130c2dcf2eb7d5edb0b18a4194616eb35c7d614e26fcab690613d9757b323d94867d0da5034a534f65584cce55bd9c5119c20d2b8431ace83236e90ccb7be0c387b11b3c916229a3bd8603bfcb9e293b7c9a2363b898382d57eed13d5ed795066b07218dab59a3ffb999172a0025c43d200d12d8a155128c4db831c24560f654e43f1a27266db9ad1d94700efab02e2f0d39c6e2a84f02523fb12d290ba50e9a1a9805b38dca8bc7363b838f11fc5fcc4a39acc6c72112807ce045be538f43e9f27c01e75d05c14f7596940ec764feb95e2e28f10390a0e74e5cb3f16140127cfa6a156fa560a3c6e13592c8cb5acce08db1c9874a24e8a9ec69e453fa9f1384d46b1f263787814072fc03ea72b8abc134a205c058e5e9b963cf4b4b043993a01abe01f4245be8b9d7f9f127e5c248ac6c5b5dd7b3454e07ded8374360d40d75a39d1d609f99c2bcec53998e407d157b88698bf2b4c830874dfa537b5660303e798d095fe35d6c3ccbbbd131305128dfc78d99bd02faaca0f03250b0ba2cf8f859000afa115aacb761780b4ccf0eabb203038ea0ba410b2444e91ae6a10d0a729763234c408b6cbaf328d74d6be6bfdd4b4bdb74bde0e49da02911bbac860f7b1ecd27981f9ba1b18d35d4db16012579d470cba0e5e117421ac0da980fb45ea23f9b1f4bfded7640d9a41767a3819ffed3d0b1ccff98ad78f63cb4548b6c487e3fa90a8e68436d8db97480afb645de0e76939f6aec3a9183ccc113ae639c4f445b4c161e47279af3abe35b80d6a971ae876e0bcdf15ddc455da007ed56c493de38182b07e1f76681a2fa9196f45c3d25f98606688b55ea6336e19638711ff00d788f16bab3baf7545061ef03f59fd3b3c69a5da63683b801868689aa56c890cba7640b3ef1806aae00be03157081b188c4bfcc045ddd65d1364bbd2f73d03d5eccff18cbfd02fef55f8259a3abb1cf3c80dae34193e155ce2d02117b2281181acad525abfcef8f617e08f3264d0cc3676b330037bef0c76d2be781d9109130dae3a2064dde40ea9103327dc858625e5c5f43636aeadfbbf08b766607aea64758115d12f3a3ccaa680c8bac7c90675b26db2aa4e437820f8843f613f9f1bfd8cb4a431bdab7ae3f2815251e6440f4575397b1362413dc0d908ac8035d7e0dc154462102269ce752caf1c0e965c80463937edade5f71dbb16559824a082fd53c65483e06fb749bec6adfe9659772b8db6d26541bf074d751096ad81e99c52d7ef4102c2646c0a220aa8b25cc7fff384b029e841c5e1d1a9663e247c9fb35838a44322b00b86c4f53843909921594d8772dd67f6fcd57b555453ec0e6a2914fb82b4f9e2fa795c99591a02f6cddcbeeff3995a26eff6d038b7c063ce6b7e95f591130710c9a83eb1d4092d30b6e3a0db2643d049eddbb6486ca3ff613045343ae99a6af052b868c84142535fcf42cd31d9c039d599e1492cce99d4dd8439330b0a2077f165fc7e1fb0a27e24eac75faefb6ebb8b825e535423f5663b09f7b157205c0302151f296af0b1d5734e1e2a8eb3cd397a8f7e9cd015e333ce73c33df4bd49e860bda526af203f82dc6b615c2ebbbaa3b99a13e45f231c133bdc755807b03311123d395bfe1a13422c38063cd2f8cc6604a3ffbad19e5bbd5776668f5b907c3f72150da121fc376b7231173de23fb4b6d2edc5989dd2e82ae695cce929259aac229968d3d266b9913cf2d577474ccbda946ddc4ad52cab0388806f364246982402ca8b651f56a0f593424f4511a776152de94b2d5e040f23eb6f50d03029a0731056d6bb00954510884059b04e580005f131b335f45dd627242e573447d24ca9317ebaa00cee1d6cd54e10693d4656d19191ea9d63f1fb58989b317a7bb4f173f02154a9ad7593c3f48daa36a36deaf60a363365cadefcb3cd7f7eedf78e07817168d8e60ae9532d27b0eb422367876aa2840738a06d8487727fb74a6e75e44f6045b19a874d8bbd97efbaf376dd343648da57e8da19a975e3c715af62bcd12cf1b40e16223afd640866cefb3d7dba53e2e6a306634692d558fdf6ca8190683c8241d67526488d47450901eee756a9ce807cf18f54e62cc823f5d47b4e3af46ca1f3b94e88bd4115fd91451db59a8e8dc0c111024a2b38af1deb3854d2994887b1037817024ab3081acfeff6b50282ba402d85b56966899fb39a6c01df6771f33044b41a29308e89a75b06b6627d85cb028d191f46bfc52df2723360f52662e99087a5737c61ee99f7cd057bf2e18689f5b747595ff1199ae6798b0499c8d75e113362ac3d589042b46807e6fbf16b6e40c8f94e444d7652dcc44d8984ad500cf1d29a2242bd30bfb5bd3eb76e7cf673275c21f9ecd128d346760169d28ab130b042b4111792bb700688ed4c313c5f168776bebd4efc7b5c8a244d8ba035bc42d039278015f319ef204e7869582a42a617cc51566356ba50ed425e8a4b749d81a0ad6f18abb1f53267cff6f65e6714c4a397efb80184e037c6327175f09bcaf7e28e8c9aee1ff608389deb8627134d0ce9fa5feb55c170e782d5214ccd605eec1257df978c84c72adec12e2b0f3126371d1ee637532aa455e2d2a78d62afdd7180045f5626941bdf1f7849adbcb24000d1c9eac6d91adadf387706d72b5dd34e804d4b52193f0dcd0d219a9063743deb5f4349336dbe6b49dbf416c1789d3d07d304e7354d6a54ead2ec2ba06b67b345473b6e1a9b1c26156c39facaf76f899e428ccb16ec1924cb31fb117f22bd7b131cbd6ea732abfc50459469e76d07dd90f1387549c3b417078677264bf1f284a099584aac71c07e7fb7363c60c868f435d20a32b528b59ce742f5afae859176c4931b55e6c0fd7e3b34fac1e1fc4116fb61da0d8cf4cb32d83588556adfc358e1130ce98576c3a350612bb36da466e5e430cd2ee1202e4ba044375f6bb5082a688a43cbc32fa463585c9f90a8572910d9a06c16854bbd5268dc754d94d49f89542e8cc8b2ef418ceb21cac5bd74938c7fa1da3952a91f4563cfccc6eb1946a3ddf30c389085a4ab95a232b732e139e86200d1a9770453f3cf095f35f9ddaea29d99de184b8123a5f2b63c0e824c16207991ad29daaa0ee898629c92a646495916aa6fd424d5015b85432cee703ad1b2aeb038313c5e1410d79237e6ca180f40c19d5e5b2513a1377ca2fac6715217ba9351fc5b3942d314b2e93c71a2f96d9155db47b6d78e1cc2bd004aa22003e163b6412a752ca2e7889cefa2751358fd0fbe3884d99f106d68c973c1dce717a0f33eb02837031a40957b5fa4835b44747471801e52586ac30295db9fc1bb1a704f18b0d1447924bf9f1e02784cc1b0532824a4c078107acd6fd9db620fd1a61a0b0ee0aaa2950bab91529b7454b666079592dc6e88fae0ed90f90d35892225d47a4862f8963dea3b555a8f01f4da08bd46ae1ae6bc16bcf7005347c8c19de6c2145ad125aa502df4cd04faff782bc862e046cc3240bf2c8bcd04f70bb4ef6d36fa27c1677f6bd1cd6b98983ddf5012b2f031398fbf6ca5c8a32da32cde2e62c75369011f86008c2d4c0ca000b7ad4499d797d6b91fa77825fb44045e75a9216249177301422c032ca94ed2749e37ac341bfcd3d25fc7ecd745e66051a06188f3f4a0b07383c49c3ef429d01566aa613cf191134cf3431342729cfbaa7732cf5482fd72aa56ab908372599856c78c0b74632d5d60966806bd67d0f82f96c58df6256021c260bcdf988a10981f18558f30407b89644a7ea76d78c0ddb151d55530b667c1343edabed74cd778cefe84306c1eab5bfebc5d97b7791300950279dc2aeae2f235b102f73827cdd964704d11d5240f22d7da11fa6afa07b841291a2c1a7ac492c757990536725bda41c4083e8acb511abb6eddeecf6e50539c4c011b3d1d10a27f1b8b43e24e1a85a932f807aae6be08145cb67c932c8a20452d5cdec07bb06169254ee048b3edeadb4c9934efcf8527debbf850f8903614da37e81f52853002fd4cfb00556968925d6b81c10197dae197b9ccfece01494971e15967e157e662a022c3517eba2f66934d09cf0610075120f622acdcec3064a52150f6d095a2724e0d64d5824673d815ea1dec20dbb5b193705c78f2b4415be9e5c2ce67e2d1d0846cb6a25cf5de0808ee27abc17f5508b07f104a3f3f0522b6b226f4b7b8b8e0e999218e897f2a6d36fb1f2dbb1e4b57ce685332a56be535c6c6cc5d257e9ff05a455f3cd0d1f8ea76967807a251b0bcb3a1cd8da51868169fc2f727c528545242850473b17de1e572da6cc3796b98847e836d80bf2ec2618287d37d544866004e27ac002f00d5ff8399f2f6c541c3afde128584ec229ff1bfd92b8fb2fedc826a12118800da2ff24c92d5f7d15f409cc23b8cccef62cade5ec8af6270aaac10e4c6b05214f6fb1f74325f1ea5d67238b2789d21a634c24e687a7636b7ce8590d2c3adb23cd6404145afddbf5e18654b398ac0a0ee354b6cfd254d9971412c302b57afb73e644896ec74bdaad3a21c268c48b37a0cf7970a22e14a95f72addf13af72ee85e567fb98daa6d94ba88d6c8af8e02bfc037fe0624c438793677591146cd48b3da4d2330c7cc82be58818b5c96ea094be4a314c8c1c3023340d48f417fdb66ff71bf86748e95ea77dfdcd27d5593241e172d745dd8ca6907f47e6b207103a1fc9bff0ba9cc5556127307bab4dd8a9cd71f62b579ae35854b736af3d2e7648230877b9611da0c0e657ffd48e0e7982e1489b6a9a37943e6cb947bd2e0abacc109c9e01f390f28eb2bc2264e2e865c46662734ab8a718d3d812c8fdfc04357462790a6d2f5ad09b11f9c4954bb8a61e7f7176ffae8ddb6733acd31fad0b5087afb2695f9eaea944cfba9505200d4fe8788a724e4aa60b165b54c35cfc2ea275976325c84249e203d793ace9ad8001a255ebfae02f564a79ad3ef9946c2dde6205a2c3b0fa8cd8e0b431bd3b0ff968a9a97cb61f2737fb6ef216ac4afb121d720587325db079dc26956af3712406d0ce60be27365339434aa7b9ee129001b0515f27e5434b9ecf0e445ee0a45d3ccd7774a54ce036ffb5177730971fc814a28953fad3b536c531909042ffa1677de8fafac8eb7db383dd9a88ea4527f70de0966b2d3bac048cc489c0c0c691913e79bc4f3ba6ac385d012978628ad122098600e73b68eddfffbafb4022683ceb1c5d689808d8f5a5fdc8ffc4730eda3426a7caf15d866e5284890ce098709e86666e178cfd0689f4d1a747666fb7e7ac084cd90565505ddf40fc09aa3e8bfe26c2db1432e60f0eb404aeea8e4249a6960a303c3fe143cb55e45564fe5b0913bf8f6c982c4c1f7649985707ee9fdbda1712afd04b600ab380d70905cee858094ca77f0b8a595709a670dd57b8faef6455179804b0479438dceab774cb4d960609046dfc54837771e26f656d613a1fb1831c84c7318ef1b588cdf7d909e123c20359af82e3d0a9bb9d1d26554b009804462a6934cef992bf0765f824d9986f3aeebed385f8731ead30635a6ce040c50618089e2e038bc669e32c3edcd131748319cae641a697f5566484a5b5504973d80b1b3f4cc0d1cf90e917b6ad033ec67e2604689987b38edff98d9ce4bf410f07f320b79c6e4192eec6ea444dc7a96af93823e5f114a0a21b343b8b6ff1a5f767ef1e0b9c2c1e64d85aa608eccc55e23d1c485f9a020aef106f2d718294e71f74ce1e49aab051f13f1142f9082466304d55db810f59e935482b661e03bf7d4200a9033ccb47c4871c61b9a42198ac6093d8d541d270d61e03a20ecad6e817cfc0ef30636283d0dcf39193aefe762da76c6e097f0a739c646ae0aea623666a3c85850c4f1220292cd457719f4071a133247a33eca153bb23a86742cd45e9f662cfc70b8dd504c56ce14ac9281add81dc778f47d84a27cc8d9560089a06c0190f50ef0bb97e18ce7eee5a4b05824fc996d8be0be1a8607a2dc26b9081af9f4c7cdc080ad4f1362d634c656a60a00a633af090d6e88ff387f306dd112205a300be4b5e175a0f41ee2865da3d79bd45526801e5032bd937f0f233f1ccebce99a43140ef234c7aefdd9c4a8f2644a9737d9ffcc545c288e8532d239c5176e230d3b93bf805c0ef1dcfffa75d1d2239524bd3a31b750b503a1e80c93d53457ab6aead7905297ec80f9ba9766b86db4d469ebb8e722662bcb84f4fb56e4030d482e9eff9d425dbade57b6986f552847e2575ae031911abf1a32e564a596ed576d3485de6401df28c94d9f4881155e58c6619602c2a468e27ba9b3df8c12199bfb949cbba410075704eba55021d5b95af43bb0cf783f84504a364fb940de65bff7eb7af405023a786e30bfd564da19f1155cc39b2d3f2a6af67b3b5204687917587c932b06a1deb041c5f0aab75a685874b5e6562c25c68e73155641b3e66e5567a553a04ef0ce570707daaa4b832abde636ff8a551512fd4c098fae9788026a28929ae4f3d09fc6fc052215b7b72f281ad73b3d4ba061286a713532fc71c54d264dd88c40a139598384a5b5090ef33fe0136e29eaa89390f81f3f46c2e61bee9b3fe89ad0025fbd0659972ec4213a2c76d87aadae18718d14abae030c5604ca57dcba8743b00bc5ef116254de78a4bbd1cbe1c0a07065296e671bcb8799caf900f24ea440f1ace537f57b619f0e5af6ab81ab1f229b22e47a9f9c6f8ddb4af9672e142cc4a172c915ae763d93adc9d47aacf9ecb9564e662c30741ec9b5c53ba3683a99ad800cb93318c412ecd8cc68042f1b2bbf47a3057d98ccffa1119780ed2975d2b031a9e72eb5031d48e4f38ab89ec302f2680e5bf6cdecdb20d9b9b3045f14c414a04ce171fd95093b88239b7b7d2705e385c0f598c8d188fb51f46f9f7405c358a2721a2fe172c8f37a88d02c0480694867d40e734f04883503f7acfff6542084c17e4a9834dbbb1f0a1d06ebb8edcb614e50f4d9d69762f94874d04d6fde4c0752d806a1a856674ce00b07a6f0db2f0406821ffe52ee7bb9eef56faf3b678a70b0056caf14c48cef88874a5d02c859c85256e1015202caec5998878c9acc0551d06eb603def682de60f7f0a6b42ee075b80ba4fae308126cd782d7907534fe71912dfd5fff73789282bdabdc9299d7baf436ca341104bc53cf83e797aa7f785101e9d723ebc25c4cff66b835fbe4041916e7465104f9b98478565eb0bfc2997fc0567e4fd5c256c85f68fac3ee24e9319f14d2405076e2a72bc71fc5f068842a71dceac206bd01bc8e72cb790af500bf02e65223546cf33ca170765f805f944070a46b061b5d11b40ace050d8cc5feb16f7c4c9b438eac11ae5981a0618f1d45c12caba43cc1af67eee753b03836dd02e4086eeebc7383e62ff80393178cddfa30efd36409f3cce15a1f3ac25691bdcb29bee5f05aadae6ad7338c675b2c4f96506539f855e4208731d60c46aeb34602b935a8f721310d9bebb0d1bc55a48947829e0aff2871271b07d65be03044bccd3983cc2991e66e1d4fb79f4a45307913326330c5496174b12a1904fc8504adbf956d3d00caf4d042a1c23d426ddfe81de04110ed4e9d82d413131da701c94510acc7894d2c5783821fb1b2947cf9b9fc815c703538175f3c0cf2faad70dda3af3dc6967bc4162e5dd3f398477700f110e24187e1ba95407833ed314bb24f38410df33bcab59155fb173c172220aedb46105fbbe750bfe20184c07aaf6ab485047c7d2fcf738e5519feac385e7d74e472e22b580993c10199c9a264fb71fd89e7cd56eefbbda1cdd63962d7d30107481532c63d7f82930de9f0e3e2c7affb5392975b7d7744702e68b1766ae7de38f60352ca38e67916736b2da3e6433726cd27b7f17e76769b44a03fe3c4e2cad5379c529ab3694223c72b67c02c60057aff7003b3e53d1ec59364a6fc561c7b4b184db2bdd79fdc9704edf0916ff4740514c1987e2f2168cc54b9dd340d02e259f99ce01ad9d5d6944dd52a978e6ba9bca1d277dd21ef3f6928cc2297ee6e76ad3256a15f1009a8956ccd68a338c2e099e5afe9b3365e5818198cb107a9f70be8add7a0ba76731923e8dc81ec699a0ef0fee7451bdf6fdd54f16e3c37bbeb1b23ab07d11a887b6a1318ce8ca89bdd326a988dd00d3b4ae59c1bbbe540469b4b3794666060925b7acc6dab414837c03d8ee123e09f87cf8a12f05c85f3ad1821edf8d12187339266d5d2a9bfa63eea5604415e1d7dfcb7cf9a3df030cdd014c5c615e340cbca315a89dd69f61b352c60c2ac8329eb51b4cacf86f3c467c0199eb1199f7093628fe7f809ceb200f8b262781c164f9e96ddb9ad897fcd1822bee0bc8df832c92537cf2cdfbc0e4d83553deb8249c5eb8bc198f1ad13f00f38e855883a4812c8692f4d277a5639d047410ad23a154c3f18b76e4b062a2f82eaa89035615db2c1f23e3ec233ad71196965522f67120d986e10b52475585d7398575cbc8a81626024b8181e049e4889d5939fea09166d9fe502e50f8e75b8a39cfeee63386890495f31496f1b60165d7afba12d993e5f3dfd22b89a1af0fb254d1151f1b263b2cc367575e069b0d0833cedd493be67d216fdec70fd7e4ea0311ffd0c622838c02297e7a5899d85528b3b21b8128abb3ee080f203fa7ef135170f4eaffc9e1fd0ba8712161528fee2c370ac0f78a6325a1f64768309a307def9f9192feabee851b6ec658ab933729028cc58961923dde03438a74bdd19ae209d65dd2a2d1b2f801c07e1b41644974aa3a9de8bb0a0054e1e4e494c2330029e9121930c07e1bf8045674837a5913cf33bd338e0903b9fb9c99a63584d83fdf0ed16c4174334f4a18138d91a0b174724252869878ed251e5dece675925eec92cdcb8215e5e4962d21989b001f106ea3d1a5143133cace9939d4bfd6b263bb3d9ff7853042f7766118940257dcbb06910a1c5809866a3b2d5d12fcce6ca65870c1f9173ff5c9603622ff578cdeae9d93d70ef9f3be40e103ba76d08fa6093dafe5c1b5590a63b9e6304326fae968c3e34f8880064566f2a96ffbe50721f2b5da52d43a5188cd710b1aef98bc4b71623fbf42b824b15a39044f7c1875e2be037b0918772b3a4b433e22a71b722d0878620af6721d59a1053c50234f4f03ca953a37a156fb6170f0190ef5bcc4e73421f17752d6857e24e8a6adb26f80c34d96bb8e613ce3918d4ddf2ab74e92651bf9c4c7be3eb2ab58693be33a8c3bfa0d51d077d9e2feb526241c058d33d0d04a34025d6513192d57dda67ec1800bca63872ed03c866eb1942db6256ab2fc551659b6a6ac38c0d3a218537151ac2497e6ac5d1a6dce982811fd2c2f72c2f6ecbc03420498f9d2d19c28416b931d31eca2a50bd70286778b5d5b3e07a9fcd192fc5c1ef2b71ae66a29d9b86c423a24c3f3d6fba8e8b6fd3a3ef2e621be45cbe7c8a823068cb6169ed79d25885594c68bb5707a95543a8dbc98c83d2593ebf226e6fa831bff91f121397319f586173e4fccfc25eace77d338669586066c1d07f923cda6df3e64acad98fa0c9bd48f3348d5891d153d215f0ef0935e04b3d439f7ae1db0ba9c09160acf0c2dd57dae351e81a229f10550f78569c69d10e0555e194d2feac7c390651f902cdc61a031e3cec9d194ae5a1abd14ad9fb3155a16e4c965278c08c26a4c65b9a7999685e602dae2f686e6eedae39af4cf3c1991c34e32bf7e092b14238f99eced1da6cbb658e72f399c5e2204cc4517284c2f2940346864a12b66ed991e5d9e54daa380c27ff63265545bf809f1cf6040c62d1dab6c55cbfbeaf1cba651c23b6e4183365896bb6a75426fecb5d7e8588625249f65de21a1fca4895de7b13d96debad13bd97edfce751359d75e90dd0ec69b1086a85f55e71d8c65942a39cd57b3ddfc66d17720f40935dfa4041ac91b02ee2e0b88602eb808552a94e2596819a2bd322aeaf970f0b9f25520c979b5ce334e1e4c41062005db6efa42f26b3afe995f3c612a3207a60cf0f86d9afb2f5c3c9e2b98ddb4cdeb681d7a79a2a9e39ee354e55db4f9489931d684aa43609c8c51b02232d661cde0880715c6d7b0d9bf48da01e99f43d720ee4b6d06cf67d1ea57472ad7287de202f92536196e5e4e7efdc289f2e8d29dae80c0ddd34e43b06b700010c6d2dd5eb40b30f5c1f34b1cbf5dd29a9c6413b404cf7f4d5e44c6efd06ebd2f61b00c6a9a23c2ee4a912a346bdeb353a24bb54066c2b48f603ce10bace6fb2b6b2f67b01b1cdf7418a0aedea179e0420b190977ad849bbf763ea19ae22a641e5416a795908c6a5bbf2fc3376ce7c9a189e4852d0a59b1b31cf12bbc26a43c0540859ec14f887cd35eade20a86905e21608136a7b9225a5aa16a9a55318071120ea66796e1e296f0eb89c9dd8a77ec55ba003d439189622adea8684f9c0d7806f7f6a167a329588ef99908d323e4038d7888fb2fd558cca93ff8a8e360148e0c47df63964e87a2f9e5dc456e9371cf436f04ad4ba531b2560a9160fcf8ffe21c8a6392a254c34a14b1fa07415cb779b4a97bfebf9c856716c497c04ee020dc2ba8db6e64064f6a12864b950b4fb8943d07c192c6b3c64636d04e920242f0b821e1e5e9468a4099bb85d0dd20a2ca0ade28850e0c75a29e17d7ea104b967dbf1a0fa7c3ca7ee2fab640e3993c5034cc9db10e99422ec8393af23fc1244823540c04d3650598e70a47652d82822dbeb095bb5422a85ebe8432d25a4b6c4bf1ff0f6522a9ab211ff1043c79af9f816db1a3ca129f5ef5b11c482f7db11867ed3405f151fedc4ecfc5d689a334802fb029a00bd9764908c84397b8ddc832d2a6ec0489befbd5fed24727e2449e82ac9a1405d9a21cd2f57451b548030bd8b00cb41b999bc3ddb385c86dee4bf26a1c90865a9cc3fb683378cb26248fa87424483120ae64deebee903fba7708211f871846015acd4d576f03d8b04662244b5a64260fded3e599ab32a66c2ed158174d3c791cf146feef2486d3458fdeb49f2d2acb27580f9291d1eff894607e2883f2efd696c8d34ac9bfd4a586b8fe056fa28f6e10aa24e33810e8b61312f869c995d4aac65dde2d094795668d54b0dc219fbc062128339cfb1a949f000a02d46388c56437439dbf3d755e85880f52510c59882120bc9892c6ca86d070eea1abe8c2566f701490a9c58012eaf3b6d6d126927348280e5413075437951f5f113bef1d4df52f41f45df426552b611545d37697ff1f0292dafb57a127f033d599a0e08071988f73608d79181ea1a4bbae27414bcdf315cebeeca215eb368de29fcfb40816567217203a1d1597dfdcd00b5220a802430a9ad88662352c218a6dcb7b7e081ec4fbd5bbd0bc983b48d405bd40db07970719874b5f0f23d5cc8242a11f54eb44441ad3afe5b59faf20a86bdc466d2af63113b7f9ca89a8532daef2d3d094372129a5beb0fa710ad73f7aff437585bfa2f2153d127117468e302cf735c858a1d9249dd51377d0c49e5c5087cf8c8e859e80187fd2efb29c999cca326d9faa50bee31813c4610afe50f2e78e140fdc05227c02223b0524a911ad80988b7784337087a40dd271d329432802b6eac492e7e57a0193fac4b5b1b03a48592cd4211acfdfa0916177c1ad875e3b7d60bea7f023b72dc0b82e002721ffc03a4e8aaa8bca823e3323373116bc9d3577f80eed7df61521e0ad3fb1dfab09b965cfa000dc910eee2e99a86ec7debedd1bfa6eba05844a1a880f9155c742f51e7b96419d512e48f56ba3c42b5d7dec49c3500b16c2084c025f86494d8ba7df6789e6ac2df2131f4ba25d5d554d75b2cdf1eeee4e44d6e421d8f4dfe37685b8da071a2112f4d690cff5e495994ac9c2af79f6d1244f984427f1e6c51dfb2011f9d1154ba3d84f2b23b52897c7f2f4a10820195af3d0be462564326ea6a7a6d7e9b4131408df2c8207e26410fbda6e2de4f8977c53fc0290", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da04570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a7110ecfce55cd472966bf92d95eaa2ca0aa89a06129ca6ea67d7b5c9e26e4925e01de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811088db0dd523e2dda90a02310bc443e2796689bb889f1a91f22c7e910915558a7279cc5790a050ab4e07fa5176b9018996360291d94e5cda28e417eb2fdd3e8df" + }, + "decryption_aggregator": { + "proof_hex": "0x0000000000000000000000000000000000000000000000040ab06fe21da21825000000000000000000000000000000000000000000000003d09f6ba158cb416a00000000000000000000000000000000000000000000000653ab0fe8456da7cb0000000000000000000000000000000000000000000000000000b0d51ec6f54400000000000000000000000000000000000000000000000971f9a84520aa31270000000000000000000000000000000000000000000000069a8682bd694d08990000000000000000000000000000000000000000000000004aac14ee1f1f9cc300000000000000000000000000000000000000000000000000023c403e139d5c000000000000000000000000000000000000000000000005840763058f90f862000000000000000000000000000000000000000000000001af162e6dd3d19c42000000000000000000000000000000000000000000000004606b05f7c092197c0000000000000000000000000000000000000000000000000002ec3674acffb5000000000000000000000000000000000000000000000000d53670d6246e537900000000000000000000000000000000000000000000000f962e8b1c78199d1300000000000000000000000000000000000000000000000a30b52d4914d59ed9000000000000000000000000000000000000000000000000000115258cf5acef2743e93d4e6374c24aef3bebcc2f4f2420e9ba7a5714ca96df52ec420a5812cc18aa6c4a565a8eae50ecf4b59ac47f274431bbfabc786534686220220083c6d426a4521a0e9e50e463baf8bba7d862eb979fee57112d10d6739cfbc5a6fd1f9512071ec987928cbd4fbaf9d86e277597b28f7f28b44afac1d78d050684ee7ab32ef47cc61e6570347351e7d14d514d6aaaa183d8f3136db330f81539a5ff03c021df9c271991b7567f82707a8ad58cc0f08a854d6c66f37ac04f4975ac8b16fa235b7da200d49e8945b4239de3bbf5257002bec0d02ca95619441e75985ca786043564090305898166319bc14b2ae8de7f23278e47910d4af9b22c1af6e4d6772bfd7385b9d88b321d75fb535fb79e9653cec5db75ad0ee5b430d3cf69f3f2e42a8c8cc6a439e517bcd51609fddca9af121f15696115028a34f01b99431f8d2d084c40dd7a68e2440be69d3c4cb4193013c9a52d743256ce32f3ae7ddb9966c82d4e413c9b46708028b32614768bf0ee52c354d61dd7ed7a6c07b4b2160877f005217f14563fcde0b9affa023d3ffc28acdf88fb52a0ab455edc3f200c41d78419495bf2c53688d2c38eb25f9b069fe0fac1156ab227d167346d68483944b5cd02d388139006df14da7dc59c66306bb63b141ffb44b153d3a2ce2be113a26fbd0f39043bbfa987f698498492d2dd5a9a107e44e8c797477789319d1c368f17d12e2ce36c2407c29941b527945fd15b8c65295e960b553088196c5c3efe8909610ac2ed3cfbfb0d8f6b42a55b7d96c1d6b876bb7eeaae3a52cb522e43842b6cf719af9dcd59ec0090750f2b640dfa3838c30b7fc60d524f21ec41e020f83136ab097d308477adc85664b412502e9e074c8ce86605f0bec32cdc0a92007bfacb4a0724afa887cb10b384a0f8a2f435e5a6a8a2476c1a43e794b5d040de6e17d96705a97dcc7110d434ef35baec043f75f16a8ad2a65ef85cbe11992a9cbb31614b1ff0f0f3ea28e6d81f4eb1d6f1370ba58e2534cb627378b8b69f6daacaace2fd158335faad367b8a5f88eacd5165afe29486713ca7d4d0a797d1b4a559d334dd13639843bb7819198343f00f242d17f223126fd1719b0817cf7fd5687b15fbd2031d179543cc51568d8451eb313243d02cab952c0817f78098276b027c46037f05df41d2b0c7bfb60da2364a6be4d4c6efc8a3bc3cfb91bb7800175e8a7049322469195f5e50994463f2ebd5690e6dd07daa9e797b5ef4cab2d13985f03f8994232deb337dd366766174a6024864c57858399fb5aa60ae7ffe0bc4f60558e5fc029d082fd155f55b3b7a1d5d11a9cc8d05db1bf23bf81781e0c95a310768386f136393e0465b8d7a920b5f46d99cfd947040ebf2edc4c9b56515fc197e7edb320d5ec04cff4e878dfda85a4d833aa29c3732cd973cb57044f72f28e220f086fb2394986b66cb4c5448a2e31fa30250ff6a017e4972a2178aaab76d92d14b59841bf47a8f314c8ed5cc73529ec625ee72879c6c70572cdfaa59a411146f53483803b645f74ae55e77770167dc9f8b9f88352d5d1405f2b2e1793f402ad14e0ea93040f6b5948c98694398711f39e7c5083d3a864d51f5d18ba87dc2604e5328931206b74e1af900f25a6159e24249b86e36c64edf56148d23e3e38eb6c415256d073fb0322559c292ab8be3d1bae23947cf6859296a20c33125166ea1e5db893200c783848aafa0b2076135f23aed7b2f6841c5d864e9ab0319b223b3df0dbff31c4ec54ef7cdce4533e6d6646188c6e79e0c62271353827e0cb821a320bedadf09e16bbcaeb45335a89d7dd4053c17ba82a9e0240b3fff9a7cd58af3f70f608d2e354669ea4f1ad977ad4ad13ababa66c1fc2794ee78c9fc4faaf890c9f2328018cf65cd2daff5027404b5baeed49e757db29c96fa3219ebae1a4a1c682c85131133f4d09ce60ca38a9cecc6a345a442cd11b6ed794e3bababd71f35c718c3b21a461b3893f154353b00e3e5c85832e6737f7100a9a5e29e421d31ff3fd1acfc177698f7e90ec5658b4e542ba9d4e202e46b6c576a1d694d7dc59c02236d052a098e5044c0f357415a36fce6e34465177fc0b914d8004ebb1ea7d7cfde7e91bd08cd0f8c745174106bea30b2acb002d58b77a984a681747c096470a038878b2619747a68514f726714f01143ba33c6067623d8d784e916716ff2da9c40f34787207b327f55b17bee72826ac64f91c43badf86e8117648079adc97e81059a5ed1283696ed822c0e5b0728ba6156a0636ad29ef805fd968b0efe04e72d174bc4dc05d38ab82149ca15c737f244b7c198deef75536eb116a6b70c99caf5a411978829028ab6b848af1a7bdf7240b46e78dfaa08f6e20a2ad9d72aefdb9b657184162d1ec631077462701554b4917f1598235d2fd2e728a6296e841f5109749dd4ac20add72426e1dd53e17d74de24f677e889c8dc52e462e9fb12989153424b619a168ef2fcbf4e23612d222a24d236a685e7ac1dee99e55a15ad5c5ccd1a90868c1f16d13b8af94831b9d998517f478b12843e79b2cf8b510e731df1e4e53d85f027ba9b775ee38e4722c1c50d840f864ad79d5b7f211bba5ace55624d9d67bf272750e7e77b265292e81e72b769ac39c69d6b87e0abf8a12905291bab45049bc9003580454d1f45cf3209f216a215f94e8fa0296039d960ca361184adaf05a5110ad6e799d393a4ec57ac1b496f0bd4c2d2d0a21704d2e807c02be4c0e65f71861ad74d91e1daae0ea9f7da1d529adb5387d14c79e9c88b2b0a5ea1354a2c451726d5453a94e0188988fcd6be18f9e0daaf1b3b4a4942c4ffe4bbe72a071ad2500f4019c931b7a61459490286f70ab69c358111ccb1062c984dca75e9be31d83f25a3f36ec88d2dfdba029caf2b502ac2d27ae6e2e4bc2c51310b577be3097b1904113b78cf1b719ea164112ef6f06d8203f3fa089bffa1864bfde3cb53dbc70d05f2887b68f9ab5de5336385f3970ac6601ca216e60bde4fb2727359a07121df2d366ccdfab360ef1f550cc116b3c4c12260c44d115662e16480f4653e71c601301100a4d931b185a9f7237ed93ffe377fb2c8a41ed83628be99e96edcf0ee871fc497dd70ba7658fb904f63552ce36737823d94cb2c86b5d043d00771798b0e137c6b475a99e2fa444f6249c5faded17fd2593e841741993be5e10a0ceb3fab13491b3cf515f2488e1ae936799cf3171105220015ed05efd0df77b02b685b850d4dc8e08ab1a1e4d84a89d55c2009f137b071b5ef47b817e8cfcfdfc318f7ee2e574cdd771653fd4ff74f23315ebec3e844c7fe91103cef4293ec6814eef68628463a2acefb7f863024e93710531118059348f66c509d87a173c331a959375d2d484ab1e6ff5834122e05bbbb8c5bcb5a3cc4f940e46e230bd2eecad42884042ae070003fdc0787eda93dbea86db1bc786f91b8124e8bcc48b53b91ad2743f316cdfb4f8af4e985375743c1ce93ddcf33243d2a785af13054b6f0a8e2d7bdce2c0f57dc1c3c616fa0cb441100f067dfdad8a704b9409c752b6a44a8192af35104d6a676ac93f8ab16780b7311c4d9e09ee52618cf47f75ddb4debfa560e325f13720280111945493a97974e573a977c4be5cd28073fc26df9e47a030a7b3cd60a9d160bbdbcf86a754657011e6221e8d3c0df4cc4f51233d4a587362f34660c209d0ce770afb06493a48ff0188e7b0f2bfdc3290970cfc07ab046a894581ca208af11ceaab5973914f6936e5e1bb5c7171e41a83758ddace922067cbda7bec528a70f53d8652e346f2811637a2bc348fa4a329543ee215284507e18213680c01b87d158aa750a942593d21665b070248eb05d9d99a3e718e83d051b80b4d0572cd3caacf4b04d9b02e7b5d119f825c51ff746f1b90690fcfff820812513341a1e518c70665d56c4fdd8026696ce962b244efd18337beb6d851371691ae548b0134e21cdd57a574975f136c4c9df4fd47242e4e8d5b4d4d5c64d434b7dc224f80ece578109ba28c79b93d58ca355122180703b48e1e65bffa0e4b8cd0834892a1af3698b86a6f19f95d7bb233b3da96574a99ae4be95be5c6a1ac80cecbaf4100cad09b438114a5de5e688f2350b6bb9628a8d529f64a0d03495a940b8af841219cbf4d848305edba7019532ed0abd817e4351dbf89985edd837bb84a253897f0f45cd9cb9704c95846fca3cbe168336a7033b9e5484e1ffae1377e5bc24ac8604d341ff988a69336a5227edf5f1ebfe4c03b8080006f1734bcedbd13c88998b04fd88933744df41cea43cf4364514c189bccdae52b745765d39cc7323e0808926e73ff470ae40e1274dfd6e4f1a1e43d34ba15f6cf124dd408631b349b3288d1bde5a64444aa6d4d969b48fb876fdab78c00be233cc6d7c2285b5d57e0d541a215591b9ba63f7a1031540a0fd2ef4b25c45401435283b8f5714a6593377fef92883a753b19056b11835d43a8dab798ae41ae19bddcc9b77e4a7d62a9044237d25debfe3113d1c32ceef3f28f255fcd7d7c1cc926ef05eb8fd38452db84907580238852d6123effb342ca4edc2e2e6cb1f3503ce645286483a24fa0d5a5203cc1901f6bf1750b0562f89622e7844fe4dc4df62f393e2df9ffc367d5559fa0eaa2e9d24c26cd530248c3612b7b92324fd60e9f2809a7714a432795348dc35e1b6157cf914b2e55748cff5db38d153c4bbfbbea5d69640d6ee322b2486d2ed27b206d734392617d64c10f7e920f8b861f2e2ff047f95f64234ab271cae6257fe270956d1443a7ffa4ed7934245c40547d4f1849536d1a325f89ab9c7068a918795136bc9fc6e672be03de71c3480bbbd5d86b9bf3271aea5ac41af91b48c52898c165691c3be42b9f43326f98e9240862a025bc5635dc1c606fc0f2509d1efef1b0fc08de20191f99e02b70875d3dab5664655fdd5290c12d141284f03b5760975043a240b864666ca80bd0d9834ba077440690d7e32958bb09128d192c4a882d80291a7bdc10b03ac9e1ba37e18351704ce1a60bc4cbdfe5396fe6973993ba8a0217669bd2f4ff5f684fdf64f00bd579194d190e52d3404eb217b7bdb07635a8704e5e9cc2eedfb4d5eac2c8c97f99a67a70b722e722545ae14a382fe970a42ce2fa136aa4acc0f44847caaec9e9b5894fce40b03f53e879fce904b96b2944c3219a345407d3b0b4701deee680e0407a6ba609db5af0239234e3038e2abe9573d1cddb19fa9e9f7cf586724b8ed082e215fdd0da169f6bfdee0c722e449c65f262b2aec80e8ee480b6410af0902bad7c53ee032e7835fe9dd4d3be4567195df882e777b9560db7442ad719eaac80f50ef874f2c54e279f3d54624ee02f47fa9501e264fe1f239017e8a52e0b02360126fcd71fcf44f9aef62d0eb47d6a2ae41e72ccdee6ac3d32aea7fd40afc96b019f5a9951a0b411e648fd40a41c1da06214401b6bd9f4b96db40cd2e3c3999220a789265f0d09426777713470380bc5bfc5e1fbbed783e3fec98db96fbd0ba69cd290c27907ef7b1982e7ef331bad447a1880722f5b7cb793fd8f27b9ed1d31dcae36e2338a2131a928c1d0e273db270b05e2f9b8b2e7e2762b41e67be8ce9aff152d20108397f3b73bcffa359b7f1ef0d4a2e963a4898c66d5706967babac451f8be3c3576e914f059af6ec9aa0952e5753019bd4cf18e64290f1c165856c17f0ba9651543a42a2ba95777c40f383d5d5311e747e15b6bbc7b2270975e0fa26e5a46291cc68074be35e4aff55b3e5ab75ea0b86348e389c8c5fd94f675becb39304de9b7aba1f5f24f9436542ac7270e91809379260ca1721fe623ed93c31cfc73290184e3fd5be777ff9748a62635aafce2400b92b1237ecf7292a17c5ac3b3d49ce2c996f9be7c74022ae9328269095f4261d23c6956f89f4d53b4dcab088be3fd495b11761c7738db37914e27d4ae9a02f40c24808c10c618690498fca7f59fb1d4ad4900940178b7cfd3c73d8a0b4a6299532a13a9c4aafe6663eedc2abb30f5dc5efd034730725391abd53c9b10d0c2e83c607da7e98c97acb9a1f579133f4cab156b61d5db4d14bdbeff7f42c65d419deb1cf3760699ead370c8b82510c0b32c579ed97d96a225c1ab0686fc6130a074eca7416092782b3271155c927f47020e8c0f1323f683cca2ed574050f733001814f79d7e5adab32768514f5804dad018d2ffc2eb9339ce639e4217b7cbd59290c6c55c4161c0fc95458c7b2caab39bd0ae622b1cd31a6facb5771fa91fdc61b8350d295d4a8ac29612ee0c7c58393f0fce2304cdd04e26b903b989a890d2512f3245c84b74f70e541548f1b43103ff12d4183ca15f6c5c4953149159f688a066190fb34a540cf37b4b011e5d56d702be708ac52a6381e6c7c14fd5e2390bf0f02054dd47b7c3130ba3b05a8358213791d14abd0942f197ff41acfd49e25000029febbf849756d5b0a1ea245c8e72a8ff5d8142e0dfbcb03d081a1e6aa523c0b9be309992df10251df3500bbefde5e398900b31a2873400259434818825128009b76de86ffbe9a22f6b0401cf2a1d31b9bc7a467edc49d08bad4a3be6a867e0369d0df4228f24157e3e74bbf98cc4fccb350cc0b1453985f35bd6d59eb3bbb0c8a708fab3e36564c0fbcf59b52b07d420c6c27b21dcbb404d78ea17ab65ab22a7ee682d495e473c0b1901da9986e35a66d58b90181b66c648ae8a057a0c65509d48727dae811979c6a30df9bb90dbb3835baf0439724bd6a16546f888067321026e81944747f1c22e9ae204cd1991984a23fcfd5310572b72d74248c91be8f1d39a8b44143e8e86c599d26faad8bdf1aaa2dfe0a5a6bbd420fb8cc4827abf30fe2a9d0839b22588fd2a0dc6cfb92fd6f86afd7f9058f6910f99e65a315a0c106958952ea6ca6135d6b09f6e67571bf4a4e4641f96965dd1706c572337152531aa504225fd8ef9d54705af04553d536d3dcfee38b3de834864252be74304ea8088fafea00475b0ad5d40f8ceab5961c9d626cafc84b26f84400357447687096293abc23a2e828c5c37c97c38f866911d9ca984a03bfa86b291bda99435500732ecc4388049288b6b777af9d4758126f2a552bcc16e75bf97bf596c0c497237f1c9f0b1ae3d97bc4531060d3ac576bc01cd9d5f10e137030e4bd33a81e16a8221e79ae12a6f7971c03de925cc87490f9b64ee9db4168f1b03d18f286ee8efbed28f9b5457b522f7446cdf3390c66ff86bc466884af706dec76517f9be11b1c4a24ad702d86399352c2f7ad293ae42d8eed275aa8ffb377b3c4f2d547deb234e62763ff0f3e58cbfdc39452771c14cbc6c8eb0e6911d53ceb304a20947766d8b415fdcd1099e3d5ae5021bc943de2cdc11ab57fa9b5a6f6b063ce886aa3eafc5d1a518044009fd94014486fc043c811bf0ef250d0ce1a073653c935db8219087c2840edcf89f08a32eef607cab2cb9140e18cc0cdcffcb023e0b26ac6691e290e01def66e7b0a31bb1110291cb92cbecbb0a5641fb9f175671ed9a114b757acc6169bebf9c9bef1c27372d5509192f4dbc0999b57d6501f2f4e04bf5be97bfb33138ccf7f178db34be5d750cd2bfd8a2375ee0ac34b4410896ad8875efb8d81131f2d5c591e39c8adab09adb775f559b695f390e934af08c8182757cf340407450bf3ab02aee67a410cff649e08fb935e1f6ef6556706d6f02464125ca1c6eb8a2db6877fb4822ab722f3b8e9bf03be6078fd699fb51125971b53a39616de119605075a795d83199b15c0d103f0c91f11395a517d1c83340c2c91aa42663d1a3e071e4414d800496e2ee7d8551acb3426677077be3c35d7f41c69d4bba4606ef7036ae83ef1b1c06905b50b2fa351b94d4be937016f6d8533368481481ca6fda30304474257de875ef5168fc4565468471c81b4c65719d4037d7cc7c835ccd80c01de4d6a992f902e74fe1b3195f1ddb53a672b331133175443811b957c1f68090dc846cc9b717234f1aae65a03dc471461efcf21e69d8be21937dca5aca848c90b98782989e7ad92e85b4f3ae502fed8f71ae2286d1336ac0d32b8a33de55273139cc206d8ffdbfba5df914eba7251435de570b239374f98276811d3469e45771db82c1754c29ac6c6e39643b34feda8dc704facf77b1eff5ef2fb8e7aa71def11a5401416d16223009f6de585be285013d644bb03ee8d9e124ff9ecd139914a2d37d47a97ef260781595c5a33bc089bf5b870b31c2cbb6e2cb96551d08fe1f100f2311a43fcca3ae43aff407034fb1b3ebd7b2a26127be42820fa97cfdb8fa02f41c7e12202331d288ff905e07293cde5fb1b4416853bb5c52bcc2120a445072554185db5bbc6c13c023e10e818333b3ea63907bdcf0275688f4480a8727b2f183dbf9242fb2dbd3320d2196863f6fb29fda90d4d090bb3afba4a59b2c68a5516cde31823fcc0d15ffd5476a12e54eb6c38b28d04a9fc2bb41e13ad2f1f2875138bed8a04299e7c714f7413e5f053e37e304248e7d969a4f994d15410f15c2b1d2aa31f2d70042e1d242c15c593a7a846a95fc96ba029425aea988212bf900e0e9b2a97f5f8b3549fed8148d271db03cf878b601a6e694024aefcc8ed72093f03a135b5fc9b72df85fab4825c6352711b7a71fc9876d66ffa5949500a6508c52518966b85d42ab90240e68488817e62085f222850ee38137f65ef5173eb9d341f47eba397a1ba71a4509aaa73f535b5e2a771a99a080ac81c2632a3096b287623a2779126dafcc6577a643972a6922fd3eb24bd3281dfb6dc1d19cd224a269f013a2861c8b8fc8036d89c1689e27a68ff32162685f45ee516f55279ed6b32372a3ca326977ec6b34cfb295fbc099ed807c2284bf81a78546681214ab14c4edf113efb51ddbbb7189ce15790f96f54615db145c0ce9e5e818a59e0ca7130a6921b9ae2514327086240dbee82447f852fbd5e599af80db39784af850b88d9060026e9833fa6c872b2c1372f93099606837851a1a168c48145d9951640a97b72092042dee789eaeec2724b826000d39dbf75ba4710212bfa31068bec37ddc16a64050095288cf1d25c9f9489012d3349074eec75085803491ebd8298924143273d1c498547e97386e641295ec8431522d17cb9dfbd2f2b4217cd83c808c7c5f18d10737bcd0f8950e8b2555b78e3f78e91834a37af5386fdae725e3f20bb54030e189b7db9b6e8ec5eff63867cba626e652be6c46d665db454445f0f83f08084af2113e5da93928627ef87936d16a7000a0a094a5002c5735caed7f84e96fc8af1114de25f98cadbda11d3a91d4d182424c842fddb7c69cf66ddf1ecf33311c9e91b198867975fea5bdccc98d7e30e8f3c8d8a12e2cf1da8a91da8373491c658ca0ac6e1bca3d1a63e41cbcb27336f3072f08b53d48c7ab0d0e077e2aaaf1ecc0e1c3898c8633a490bbed7d87f0b648dc8f0f18c049f79f8510814b0513f9716542796fad753e5425f502639f66f85280d9a026609045e5ce3d85d58cddfa0760f2da4146c8424d471d5bd5992a8fbc67b3bc24a107b83575ea4ec667c8684d4a50d879310cf292b4176fd2da386d2827c6a949c75f45c1af3841c6742811f4e532b67908e68172683560e3ba9deff96afa53543c7525ab1e2572c675585600a3c0e8d9cccbdf1d2dba272332cfbf5dcdbf4598ca883d526f0348b407a0b53b7f72d63519b8a3fd6b14bf1bbe4d0caea0eb71f6303aeaf5198f0b96c540561c7b024593b5c3bc10bdef4eeb418bd7d40e811b46a10d85eb1d03201b1174418ebef2f8d48c74be847aaad4d92bb1c1a4905fb0fad9118f778ded6e9f18e291e974118d6dfb276396599d87ac39e61fbbd9acd173234149be49b533d3329a6a471a00d06013725130cab4546ae6063008eb76f952a8f7b1d4bc364948346145a1d512950eb1ba9ddff18f9a5a6b46dd0375ce99fa87e59b41ca52fa1956a5f5b54851cada72169d4dce1449ab2efb0c6b9ac9e152462a4b94938d0e02d656b2b302516047fd71cfbc6fc5f71c30e004e4e77fab46d560bb462bdb52508506099fbda2a132413027e013ca6de54dde3497cd6f4e165855c4daf55a23da099c2ab11422b7037249598689857c3181330d7c7c0c85723bebf2fd38ab79ff15ab71c736e070cce892a0e995f3eaeeafa42934a50cd1c3f68cf168c50c2541e7722e08b5a1952dc2def02fc665ba4ea42f9820ec05e6f622ac1445b710685b911d67280240ab866edeab9a93f511c74e06d9409f43ea822de953e04b405e4e258f3cb641e1d51e5848cf19ba0090c881d58acc9d09c8122626a807299cd251dea77bfdc6c1db5a608d1d7f90441ac3da7a850b39d37ba055d46822b7f4a440b369ee3dbfe0e2e4afb5a995b21b025423a3421ea55f22a69812efe31e835d00ffbaa26115c1e6fcc35e3bc1ecaa89b717d2acd42f0ace83fb1e88c08230658c64331a0d3c002205b52ce6a9a3afcfdb7fd07f0ff917b0682a827e6be0e519de443bd7855c028ec2309d0e2f5f228b984c5874b5c1ad10d7bd97a10bac7b9828e46ed4b5a281205f36b20d02c74c43cb5a415a3785ca8bfb4d3d4d2ec5816901120511ac4f82ddf777d020409db617ffc7d064710afe1af7171ab23eb3a2827242a81ac15a00c068ef21b34310cc14da591ad18e6e044f3eaace0ee07bd05ecbf722a7b6c292bc6f2d578c6d7d9c998f997dfc03a913dde3e1d965b17f32593a5a0627e0c092c10fda42bc30f3ce904e4c26eff37953802d60afce5cbb470bb6d15412e268f3023a4042bc161e38fd8e621f9d459f3effe4110dc22da5fa1868bd3918cf7930d18599d713f0b6c205283389b0f2453340975f771defb2b5e31c3857dc90fd92daf36046a3cb64afcd9050f4bbb9fa89428624a066a44dc8693c560b09ba22b0a418603118fbf9d862084533831534afd2aaec3cb75aaaf22b396d7188bfb0e0b3709fb4ce0a08b7dd002572a13ec48a5a480dbaee3570d1fce629dc5d0e56b21cffbd42d18cd9b62dd78c23ffc8d2f5ae1c467b80afb45c687c9dc954e8f321b43ddb100a18f3af3880a51969626dc3e15ba3bc4980e171184ee3bd89dee052639becb1659d8c12e42e0652b6a33d148999ce6153d4f4e7afc0e640bb231ca2a59dd79ce91efcc51caf5495bf45e00df2beb4120e7d30d2f5a0446206fb6e3196ba3c835908bd1a2bc0754ab6bc4819f2f5210fa494f76792070e5e2c454be02b1da704e87aa8489ddb9340f8841d94446eb311f21bd78713ae2498aaae1631834d8fede4f068b2a340d75f9e37b796a0d014d265179ae4f24c0e79811981d2ef1b18f66869e914aef1780d5bd351e58eee8423b18cc9314079f6aa2f344601c8b4a05c7631d77da06ced0416667ba392875d9af73a3157ef63673783dc13826a144f350975fa8223473bce2d7337890bd3cf3e56eccdaa2928923b06eb9f5178761d6ee4c5c09d2c2970c06cff0a9baab2d0718f5db3f61455817fe60a7862f8573382c9e2d1eb09bdd03bb2af0db6344c045fb86e12579a8205b460b1c0312cf88883ca725d6b7319535c10442a31afbb47781e7b486a30e4c35a016770c23790213fc854527c7dba54a75a9de6b88d741433c32774d547ef5e9d86d1d76125a9c41bfe6c1c5414b4954c5ddd09c8830eb22edfa6888f76e6f227358ce321c8233821ebcf0fd330a038028d95c8555be7bfe7e30a6e9a95934ca77461df027d3e0d2c5716339668a42412bbbc5e10dd51fe22a7ab706d88361503c99d4ef0b15a392a838b5c3b67c251972cd268b60c15d3fbbd6bebd225ee236d219d942076f9d9bbfb77981cbce154b5b9b55a15e74416b9af9b1dccc957bafc744c124277d650e13f2839ee6faa36b0a3b4db8597de30e4b95efba2d6aae287df1c67b1eac34ac25df0fa2360d47b727bf22d4cff0745872825394be099fe22b57a12614ed7350f7e16da6c81c07dfdec5c33efe7b6486e06b96a4659ec6fd8671f6a82c55903d7c139842b424d91666571c68928cea428bc1d612d6e7f44bf7c27d0f17ed583db00056b3382cf91233026547ebe5f93f1aad6309cfc2e419a7c179520e99025f7f20d9951f7b11b0319944d7d18c1636bcfea5f034be45a58632372204770cb1ac257d09f1a6a07837916260885ea279463d727b331b264b1f0b49381f7f2e77f8cc6a69171a6a7ad635e415ca81bf32489b92644e873a4fc170ffe1117c1dfb8f1d10c543a1d9047564d769469816930e3f41854864298374980cde254352efbe0abd397db0fd78222d71595961d9981c996d5ef96383dbbb766b840876fa3e4dbfee7486a0199e40a93774fcc202ca7e094b1bb99bb5c03d5128a626564e425bac7f3eac2991821756e078e406f16d5fa573f59897d0d345244d590b3d34d64df3afc81bacfd30b9e8164ed6e043ec5250231e37f9a6a8f652bd7f0fbaf17b908d410d6d947d7fca1d3b51ace99f755928d9aeea46034d972a296d25beb92b01ebe662acc722dc0eb9b8ad40227e67acc989bcc820773e1c83aa612905aefce55491f1925af1c04f0f231be3514324a179e5315a1bb37dae95a5661f956272bb735d7b95e89c33e7c01be2b7e83582ae1a355e9df4ecc315e84dc62ab2c1206457dc7ce8ec99f837075700053651b11109c8627bbc53705b593bd91c3da4df434d327d486cd25a916b8607d33f31ec6b9e9bff15eed344c0fe88280d877a5b4bcdb00799f7837455cd919fddd991bfdd02dda98cfc21db8b9b880e0fe664cd7591257dcc7cc3e0c131721b88d6fc684e93073ba5d5b050eba773ee1f1917ab154ba311a46e1de3a8818934b0984ceb63d6f8a0e8fdd1e88f8ccb8521fe6811cbc580e23c1ffea1b329d7e51865a34d1c0fb717e870a221cdf9390a1815630d082b4327aaaa31d4e2c343fe24db2ee45cfb6209e7b3f052f65ea94a05769a31ac312b65e8b702576524d2e51da83c3511a1b211e67cf750a2e2a63a003f5e68ab24af990d658fa911e9b16d48e2dd4c39a5420208eab00611a2952c14a3adf3a46fd71d7dff35d4a329ae6edf88bf82ce63767a264c0f55f58152e12f9215f4f60bcf23a87a052d4ff98d4ea15397227ef1a31e27630c2598afeb781697c705ff91abd6ef567672b21af372848a5b907b137380b6f35578fa10c0442f8e8a9c1185d5390101051a7bcb9a9cc26e54dfc06dce51a827d771e712c4e726a462eb20b0b67b110f61c511ac1d1768c17bb76bd94f4687e926e91d8d326a169341d5a6c63d0f4d2a4657014e2d32ef9e2605e83ffff451efd4e44c31e4331cd7e76c07fe587393bd2896c20f496a1284126981e6dd39c9a93489dea6aad722589bcf9152050f6fc89e1c9bdbd1cd00b14c92dac4b0888dce9deb64d34b6e2799104440d4688bdbdf16aa80a0032eae6ecf4f5457dc7ec502f9d6541e57f11d0a33df49cb75b224d68297662a24fcad6c6c4a1d058b66b52e5194be351854214297bd50145c5117c2046294d80ab362f32cefafe752aebb35e18b6343202b088a5e3740209e74a2dfe097771d91abbf91821bd6d8d2775370bfe80c3b05a20b28e7e4a875a7f7fcfb34543326cca4cf25a9711579795fe1cc240533bb43bc25531a47002615f2a582ae9b97af5ab449ee49ebf1524fc70284c5db9d9ab8d60cd6a756b5b14120ec32650a922f09b9ff0e05191d9cb64e8a49bc9ba9ff38862f8438c9f72d8de501a998b72b3e094cee71964c093774e6cde95231bc7488af29ef82fc95f20b73e22cf7397dc2f815c98cc0ab35ce3aa2573b3bfa52c1c4e20b14ce9085edd1ba3efd5b4e7eae79db51e0c0b6eccc2d8c2fe017b1e953050b10865d5f69252f27099e8607385efc6b34356105e0d55711c256623bf827cd6601ebdd0093ccf8b5b7c22a4a7c023a8f3f6f9c2e033af6e983488fe17ef544b70afbb9a6c7baf14c4c66d617464ae680959258f3d6d0921d8220bfcdf54a201307980f3e5d8997783a5cc6260d02bef3906564b64e944d925cb366b68aeae3762bfb562400014dcb231e337e4cb1835d935a6ceef1cb72c2bf7ac05b9b7063b82b65f37d1d1f7836b8339d8b2588c630d786c8a0cf090161b1d37814363803581fa5ce82abb747da6d6c34a0260e627bebd1d4776e9e328fafb6d1383fb9b2062ee54c659b246a40a81b5eb1901216be06a9a6da8aa2fcba770d5ce232f8bb551259bb0dcab25477febc5d2db3124082c3fa025144e7ff3a2f56d6ba87a7d2df10fe1067d51a1356857e5a675b99f2573747dc685678c1896d6d820d5fbb6a142365320332eba438ef44aa656caa7815f18b3f9c59eb23e8b60deff463beccff1f48996cee11a6a538526d034a9b60f065fe0f5039bf073640b2ec41db6fbac5193ea88028976c924952c5225f55aa1affac5e7586245ba59dd8a2df0bc13af700e67b6fd740d52b6bec53ad840a94f31169d6dadbe51fc56a45b3fb95b7363e06027ec79d84a0751501e4ce5afbfd2be1117af68a171593a2ae7f2517eeaf3d0a26ef542845f9179278f756a8155454f79686846fc0527bbd8cd9f42ef1287e06eb851df7c68cb72bc28b11fb26f5a0302f4d1431d9bfde843ef7c022000d9322e65464fc3fd94083d6b1d693fc9bcd311e078cc6fb4bf837c86b1336509b0c03785ca3629140210823f3a34e477e6b53113a069203acc49a6df56466be294f", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000204570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a701de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } +} diff --git a/circuits/benchmarks/results_insecure_agg/report.md b/circuits/benchmarks/results_insecure_agg/report.md new file mode 100644 index 000000000..1125dc797 --- /dev/null +++ b/circuits/benchmarks/results_insecure_agg/report.md @@ -0,0 +1,213 @@ +# Enclave ZK Circuit Benchmarks + +**Generated:** 2026-05-22 14:30:45 UTC + +**Git Branch:** `feat/1549` +**Git Commit:** `f4cce353bc4f70cd6e3f82e4091a3fee89b67286` + +**Committee Size:** `H=3`, `N=3`, `T=1` + +## Run configuration + +Settings for this benchmark run (integration test + Nargo circuit benches on the same host). + +### Integration test (`test_trbfv_actor`) + +| Setting | Value | +| ----------------------------------------------------- | -------------------------------------------- | +| Benchmark mode | `insecure` | +| BFV preset (artifacts) | `insecure-512` | +| BFV preset (enum) | `InsecureThreshold512` | +| λ (smudging / error) | 2 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | true | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | + +### Hardware & software (Nargo / Barretenberg host) + +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **CPU** | Apple M4 Pro | +| **CPU cores** | 14 | +| **RAM** | 48.00 GB | +| **OS** | Darwin | +| **Architecture** | arm64 | +| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | +| **Barretenberg** | 3.0.0-nightly.20260102 | + +--- + +## Audit status + +On-chain verify gas: **complete** (CRISP Π_user + Enclave Π_DKG / Π_dec replay). + +--- + +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus +(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare +runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, +commit, and hardware. + +--- + +## Protocol Summary + +### Circuit Benchmarks (isolated Nargo + Barretenberg) + +Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). + +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | +| -------------------- | ----------- | --------- | ----------- | ---------- | +| C0 | 6847 | 0.13 | 27.07 | 15.88 | +| C1 | 57818 | 0.33 | 25.03 | 15.88 | +| C2a | 142625 | 0.83 | 25.68 | 15.88 | +| C2b | 198355 | 0.90 | 26.57 | 15.88 | +| C3a | 132633 | 0.78 | 25.49 | 15.88 | +| C3b | 132633 | 0.78 | 25.49 | 15.88 | +| C4a | 92515 | 0.50 | 25.70 | 15.88 | +| C4b | 92515 | 0.50 | 25.70 | 15.88 | +| C5 | 151717 | 0.80 | 25.71 | 15.88 | +| user_data_encryption | 53732 | 0.34 | 25.17 | 15.88 | +| C6 | 86927 | 0.52 | 24.84 | 15.88 | +| C7 | 104273 | 0.51 | 25.71 | 15.88 | + +### Artifacts + +| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | +| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | +| Π_DKG | 10.69 KB | 0.47 KB | 3042548 | 176196 | 3218744 | +| Π_user | 15.88 KB | 0.12 KB | 2973097 | 170416 | 3143513 | +| Π_dec | 10.69 KB | 3.47 KB | 3553726 | 187308 | 3741034 | + +### Role / Phase / Activity + +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 159.57 s | 127.00 KB | 128.19 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 131.58 s | 10.69 KB | 11.16 KB | +| User | P3 | per user input | isolated_nargo | 0.65 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.52 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 68.10 s | 10.69 KB | 14.16 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 57.87 s | 10.69 KB | 14.16 KB | + +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **24.69 s** — not +comparable to P2 wall_clock row above._ + +## Integration test (`test_trbfv_actor`) + +### End-to-end phase timings (integration test) + +| Phase | Metric | Duration (s) | +| ------------------------------------------------------------------ | ------------ | ------------ | +| Starting trbfv actor test | `wall_clock` | 0.00 | +| Setup completed | `wall_clock` | 3.04 | +| Committee Setup Completed | `wall_clock` | 20.25 | +| Committee Finalization Complete | `wall_clock` | 0.01 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 131.58 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 159.57 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 162.13 | +| Application CT Gen | `wall_clock` | 0.31 | +| Running FHE Application | `wall_clock` | 0.00 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 57.87 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 68.10 | +| Entire Test | `wall_clock` | 253.85 | + +### Multithread job timings (`tracked_job_wall`) + +| Name | Avg (s) | Runs | Total (s) | +| ----------------------------- | ------- | ---- | --------- | +| CalculateDecryptionKey | 0.12 | 3 | 0.37 | +| CalculateDecryptionShare | 0.63 | 3 | 1.88 | +| CalculateThresholdDecryption | 0.56 | 1 | 0.56 | +| GenEsiSss | 0.17 | 3 | 0.50 | +| GenPkShareAndSkSss | 0.24 | 3 | 0.72 | +| NodeDkgFold/c2ab_fold | 7.62 | 3 | 22.87 | +| NodeDkgFold/c3a_fold | 35.48 | 3 | 106.43 | +| NodeDkgFold/c3ab_fold | 7.55 | 3 | 22.65 | +| NodeDkgFold/c3b_fold | 35.65 | 3 | 106.94 | +| NodeDkgFold/c4ab_fold | 7.20 | 3 | 21.59 | +| NodeDkgFold/node_fold | 17.67 | 3 | 53.02 | +| ZkDecryptedSharesAggregation | 8.41 | 1 | 8.41 | +| ZkDecryptionAggregation | 49.42 | 1 | 49.42 | +| ZkDkgAggregation | 20.37 | 1 | 20.37 | +| ZkDkgShareDecryption | 3.17 | 6 | 19.02 | +| ZkNodeDkgFold | 111.17 | 3 | 333.52 | +| ZkPkAggregation | 4.33 | 1 | 4.33 | +| ZkPkBfv | 0.43 | 3 | 1.30 | +| ZkPkGeneration | 5.17 | 3 | 15.52 | +| ZkShareComputation | 7.41 | 6 | 44.47 | +| ZkShareEncryption | 7.47 | 24 | 179.24 | +| ZkThresholdShareDecryption | 7.68 | 3 | 23.03 | +| ZkVerifyShareDecryptionProofs | 0.11 | 3 | 0.34 | +| ZkVerifyShareProofs | 0.24 | 5 | 1.21 | + +Sum of tracked job wall time: **1037.71 s** — **not** end-to-end latency (jobs run in parallel up to +`BENCHMARK_MULTITHREAD_JOBS`). + +### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) + +| Step | Avg (s) | Runs | Total (s) | +| --------- | ------- | ---- | --------- | +| c2ab_fold | 7.62 | 3 | 22.87 | +| c3a_fold | 35.48 | 3 | 106.43 | +| c3ab_fold | 7.55 | 3 | 22.65 | +| c3b_fold | 35.65 | 3 | 106.94 | +| c4ab_fold | 7.20 | 3 | 21.59 | +| node_fold | 17.67 | 3 | 53.02 | + +### Aggregation jobs (`tracked_job_wall`) + +| Operation | Avg (s) | Runs | Total (s) | +| ---------------------------- | ------- | ---- | --------- | +| ZkDecryptedSharesAggregation | 8.41 | 1 | 8.41 | +| ZkDecryptionAggregation | 49.42 | 1 | 49.42 | +| ZkDkgAggregation | 20.37 | 1 | 20.37 | +| ZkNodeDkgFold | 111.17 | 3 | 333.52 | +| ZkPkAggregation | 4.33 | 1 | 4.33 | + +Sum of aggregation job tracked time: **416.04 s** (parallel CPU work; not P1/P2 wall clock). + +### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) + +| Artifact | Proof (bytes) | Public inputs (bytes) | +| --------------------- | ------------- | --------------------- | +| dkg_aggregator | 10944 | 480 | +| decryption_aggregator | 10944 | 3552 | + +## Raw circuit benchmark JSON (Nargo) + +Source files for the **Circuit Benchmarks** table. Persist this directory with +`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without +re-running the integration test. + +| File | +| ----------------------------------------------------- | +| `dkg_e_sm_share_computation_default.json` | +| `dkg_pk_default.json` | +| `dkg_share_decryption_default.json` | +| `dkg_share_encryption_default.json` | +| `dkg_sk_share_computation_default.json` | +| `threshold_decrypted_shares_aggregation_default.json` | +| `threshold_pk_aggregation_default.json` | +| `threshold_pk_generation_default.json` | +| `threshold_share_decryption_default.json` | +| `threshold_user_data_encryption_ct0_default.json` | +| `threshold_user_data_encryption_ct1_default.json` | + +## Notes + +- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is + effectively 0. diff --git a/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json b/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json new file mode 100644 index 000000000..ffa8b6ee6 --- /dev/null +++ b/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json @@ -0,0 +1,11 @@ +{ + "benchmark_mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "proof_aggregation": false, + "multithread_jobs": 13, + "verbose": false, + "nodes_spawned": 20, + "committee_size_n": 3, + "network_model": "in_process_bus", + "testmode_harness": true +} diff --git a/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json b/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json new file mode 100644 index 000000000..1a64e490c --- /dev/null +++ b/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json @@ -0,0 +1,88 @@ +{ + "verify_gas": { + "dkg": null, + "user": 2972965, + "dec": null + }, + "source": "folded_proof_export_plus_crisp_verify_test", + "artifact_sizes_bytes": { + "dkg": { + "proof": 0, + "public_inputs": 0 + }, + "dec": { + "proof": 0, + "public_inputs": 0 + } + }, + "calldata_gas": { + "dkg": { + "proof": 0, + "public_inputs": 0, + "total": 0 + }, + "dec": { + "proof": 0, + "public_inputs": 0, + "total": 0 + } + }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "bfv_preset": "InsecureThreshold512", + "lambda": 2, + "proof_aggregation_enabled": false, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": false, + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.129886722, "runs": 3, "total_seconds": 0.389660166 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.623894805, "runs": 3, "total_seconds": 1.871684416 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.554217625, "runs": 1, "total_seconds": 0.554217625 }, + { "name": "GenEsiSss", "avg_seconds": 0.150156208, "runs": 3, "total_seconds": 0.450468624 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.240268944, "runs": 3, "total_seconds": 0.720806833 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.443416042, "runs": 1, "total_seconds": 8.443416042 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 2.964831979, "runs": 6, "total_seconds": 17.788991876 }, + { "name": "ZkPkAggregation", "avg_seconds": 2.12222325, "runs": 1, "total_seconds": 2.12222325 }, + { "name": "ZkPkBfv", "avg_seconds": 0.433216972, "runs": 3, "total_seconds": 1.299650917 }, + { "name": "ZkPkGeneration", "avg_seconds": 4.951818527, "runs": 3, "total_seconds": 14.855455582 }, + { "name": "ZkShareComputation", "avg_seconds": 7.233157423, "runs": 6, "total_seconds": 43.398944541 }, + { "name": "ZkShareEncryption", "avg_seconds": 7.581401666, "runs": 24, "total_seconds": 181.953639998 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 7.736885083, "runs": 3, "total_seconds": 23.210655251 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.10097575, "runs": 3, "total_seconds": 0.30292725 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.227720524, "runs": 5, "total_seconds": 1.138602624 } + ], + "operation_timings_total_seconds": 298.501344995, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, + { "label": "Setup completed", "seconds": 3.065066, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.247525958, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.005479542, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 2.132179, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 29.416552334, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 31.983316666, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.307567583, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.003297708, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 8.471012, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 18.676276166, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 74.289529916, "metric": "wall_clock" } + ], + "folded_artifacts": null + }, + "test_exit_code": { + "crisp": 0, + "folded_export": 0, + "enclave_contracts": 0 + } +} diff --git a/circuits/benchmarks/results_insecure_no_agg/integration_summary.json b/circuits/benchmarks/results_insecure_no_agg/integration_summary.json new file mode 100644 index 000000000..898ab8a0b --- /dev/null +++ b/circuits/benchmarks/results_insecure_no_agg/integration_summary.json @@ -0,0 +1,180 @@ +{ + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "insecure", + "bfv_preset_subdir": "insecure-512", + "bfv_preset": "InsecureThreshold512", + "lambda": 2, + "proof_aggregation_enabled": false, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": false, + "multithread": { + "rayon_threads": 13, + "max_simultaneous_rayon_tasks": 13, + "cores_available": 14 + }, + "operation_timings": [ + { + "name": "CalculateDecryptionKey", + "avg_seconds": 0.129886722, + "runs": 3, + "total_seconds": 0.389660166 + }, + { + "name": "CalculateDecryptionShare", + "avg_seconds": 0.623894805, + "runs": 3, + "total_seconds": 1.871684416 + }, + { + "name": "CalculateThresholdDecryption", + "avg_seconds": 0.554217625, + "runs": 1, + "total_seconds": 0.554217625 + }, + { + "name": "GenEsiSss", + "avg_seconds": 0.150156208, + "runs": 3, + "total_seconds": 0.450468624 + }, + { + "name": "GenPkShareAndSkSss", + "avg_seconds": 0.240268944, + "runs": 3, + "total_seconds": 0.720806833 + }, + { + "name": "ZkDecryptedSharesAggregation", + "avg_seconds": 8.443416042, + "runs": 1, + "total_seconds": 8.443416042 + }, + { + "name": "ZkDkgShareDecryption", + "avg_seconds": 2.964831979, + "runs": 6, + "total_seconds": 17.788991876 + }, + { + "name": "ZkPkAggregation", + "avg_seconds": 2.12222325, + "runs": 1, + "total_seconds": 2.12222325 + }, + { + "name": "ZkPkBfv", + "avg_seconds": 0.433216972, + "runs": 3, + "total_seconds": 1.299650917 + }, + { + "name": "ZkPkGeneration", + "avg_seconds": 4.951818527, + "runs": 3, + "total_seconds": 14.855455582 + }, + { + "name": "ZkShareComputation", + "avg_seconds": 7.233157423, + "runs": 6, + "total_seconds": 43.398944541 + }, + { + "name": "ZkShareEncryption", + "avg_seconds": 7.581401666, + "runs": 24, + "total_seconds": 181.953639998 + }, + { + "name": "ZkThresholdShareDecryption", + "avg_seconds": 7.736885083, + "runs": 3, + "total_seconds": 23.210655251 + }, + { + "name": "ZkVerifyShareDecryptionProofs", + "avg_seconds": 0.10097575, + "runs": 3, + "total_seconds": 0.30292725 + }, + { + "name": "ZkVerifyShareProofs", + "avg_seconds": 0.227720524, + "runs": 5, + "total_seconds": 1.138602624 + } + ], + "operation_timings_total_seconds": 298.501344995, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { + "label": "Starting trbfv actor test", + "seconds": 0e-9, + "metric": "wall_clock" + }, + { + "label": "Setup completed", + "seconds": 3.065066, + "metric": "wall_clock" + }, + { + "label": "Committee Setup Completed", + "seconds": 20.247525958, + "metric": "wall_clock" + }, + { + "label": "Committee Finalization Complete", + "seconds": 0.005479542, + "metric": "wall_clock" + }, + { + "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + "seconds": 2.132179, + "metric": "wall_clock" + }, + { + "label": "ThresholdShares -> PublicKeyAggregated", + "seconds": 29.416552334, + "metric": "wall_clock" + }, + { + "label": "E3Request -> PublicKeyAggregated", + "seconds": 31.983316666, + "metric": "wall_clock" + }, + { + "label": "Application CT Gen", + "seconds": 0.307567583, + "metric": "wall_clock" + }, + { + "label": "Running FHE Application", + "seconds": 0.003297708, + "metric": "wall_clock" + }, + { + "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + "seconds": 8.471012, + "metric": "wall_clock" + }, + { + "label": "Ciphertext published -> PlaintextAggregated", + "seconds": 18.676276166, + "metric": "wall_clock" + }, + { + "label": "Entire Test", + "seconds": 74.289529916, + "metric": "wall_clock" + } + ], + "folded_artifacts": null +} diff --git a/circuits/benchmarks/results_insecure_no_agg/report.md b/circuits/benchmarks/results_insecure_no_agg/report.md new file mode 100644 index 000000000..75a1fe606 --- /dev/null +++ b/circuits/benchmarks/results_insecure_no_agg/report.md @@ -0,0 +1,186 @@ +# Enclave ZK Circuit Benchmarks + +**Generated:** 2026-05-22 14:49:38 UTC + +**Git Branch:** `feat/1549` +**Git Commit:** `f4cce353bc4f70cd6e3f82e4091a3fee89b67286` + +**Committee Size:** `H=3`, `N=3`, `T=1` + +## Run configuration + +Settings for this benchmark run (integration test + Nargo circuit benches on the same host). + +### Integration test (`test_trbfv_actor`) + +| Setting | Value | +| ----------------------------------------------------- | ---------------------- | +| Benchmark mode | `insecure` | +| BFV preset (artifacts) | `insecure-512` | +| BFV preset (enum) | `InsecureThreshold512` | +| λ (smudging / error) | 2 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | unknown | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | + +### Hardware & software (Nargo / Barretenberg host) + +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **CPU** | Apple M4 Pro | +| **CPU cores** | 14 | +| **RAM** | 48.00 GB | +| **OS** | Darwin | +| **Architecture** | arm64 | +| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | +| **Barretenberg** | 3.0.0-nightly.20260102 | + +--- + +## Audit status + +> **Incomplete on-chain verify gas:** 2 of 3 artifact verify-gas values are **N/A**. Re-run +> `./run_benchmarks.sh` and ensure `extract_crisp_verify_gas.sh` completes (CRISP test + +> `test_trbfv_actor` + EVM replay). Calldata gas alone is not sufficient for audit sign-off. + +--- + +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus +(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare +runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, +commit, and hardware. + +--- + +## Protocol Summary + +### Circuit Benchmarks (isolated Nargo + Barretenberg) + +Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). + +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | +| -------------------- | ----------- | --------- | ----------- | ---------- | +| C0 | 6847 | 0.12 | 25.14 | 15.88 | +| C1 | 57818 | 0.36 | 25.40 | 15.88 | +| C2a | 142625 | 0.83 | 26.78 | 15.88 | +| C2b | 198355 | 0.85 | 24.42 | 15.88 | +| C3a | 132633 | 0.81 | 31.38 | 15.88 | +| C3b | 132633 | 0.81 | 31.38 | 15.88 | +| C4a | 92515 | 0.48 | 24.11 | 15.88 | +| C4b | 92515 | 0.48 | 24.11 | 15.88 | +| C5 | 151717 | 0.79 | 27.23 | 15.88 | +| user_data_encryption | 53732 | 0.32 | 25.77 | 15.88 | +| C6 | 86927 | 0.53 | 24.62 | 15.88 | +| C7 | 104273 | 0.49 | 25.28 | 15.88 | + +### Artifacts + +| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | +| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | +| Π_DKG | 15.88 KB | 0.12 KB | N/A | 179524 | N/A | +| Π_user | 15.88 KB | 0.12 KB | 2972965 | 170392 | 3143357 | +| Π_dec | 15.88 KB | 3.25 KB | N/A | 188220 | N/A | + +### Role / Phase / Activity + +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 29.42 s | 127.00 KB | 128.19 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 2.13 s | 15.88 KB | 16.00 KB | +| User | P3 | per user input | isolated_nargo | 0.64 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.53 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 18.68 s | 15.88 KB | 19.12 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 8.47 s | 15.88 KB | 19.12 KB | + +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **2.12 s** — not +comparable to P2 wall_clock row above._ + +## Integration test (`test_trbfv_actor`) + +### End-to-end phase timings (integration test) + +| Phase | Metric | Duration (s) | +| ------------------------------------------------------------------ | ------------ | ------------ | +| Starting trbfv actor test | `wall_clock` | 0.00 | +| Setup completed | `wall_clock` | 3.07 | +| Committee Setup Completed | `wall_clock` | 20.25 | +| Committee Finalization Complete | `wall_clock` | 0.01 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 2.13 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 29.42 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 31.98 | +| Application CT Gen | `wall_clock` | 0.31 | +| Running FHE Application | `wall_clock` | 0.00 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 8.47 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 18.68 | +| Entire Test | `wall_clock` | 74.29 | + +### Multithread job timings (`tracked_job_wall`) + +| Name | Avg (s) | Runs | Total (s) | +| ----------------------------- | ------- | ---- | --------- | +| CalculateDecryptionKey | 0.13 | 3 | 0.39 | +| CalculateDecryptionShare | 0.62 | 3 | 1.87 | +| CalculateThresholdDecryption | 0.55 | 1 | 0.55 | +| GenEsiSss | 0.15 | 3 | 0.45 | +| GenPkShareAndSkSss | 0.24 | 3 | 0.72 | +| ZkDecryptedSharesAggregation | 8.44 | 1 | 8.44 | +| ZkDkgShareDecryption | 2.96 | 6 | 17.79 | +| ZkPkAggregation | 2.12 | 1 | 2.12 | +| ZkPkBfv | 0.43 | 3 | 1.30 | +| ZkPkGeneration | 4.95 | 3 | 14.86 | +| ZkShareComputation | 7.23 | 6 | 43.40 | +| ZkShareEncryption | 7.58 | 24 | 181.95 | +| ZkThresholdShareDecryption | 7.74 | 3 | 23.21 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.30 | +| ZkVerifyShareProofs | 0.23 | 5 | 1.14 | + +Sum of tracked job wall time: **298.50 s** — **not** end-to-end latency (jobs run in parallel up to +`BENCHMARK_MULTITHREAD_JOBS`). + +### Aggregation jobs (`tracked_job_wall`) + +| Operation | Avg (s) | Runs | Total (s) | +| ---------------------------- | ------- | ---- | --------- | +| ZkDecryptedSharesAggregation | 8.44 | 1 | 8.44 | +| ZkPkAggregation | 2.12 | 1 | 2.12 | + +Sum of aggregation job tracked time: **10.57 s** (parallel CPU work; not P1/P2 wall clock). + +_No `folded_artifacts` in integration summary (export failed or test exited early)._ + +## Raw circuit benchmark JSON (Nargo) + +Source files for the **Circuit Benchmarks** table. Persist this directory with +`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without +re-running the integration test. + +| File | +| ----------------------------------------------------- | +| `dkg_e_sm_share_computation_default.json` | +| `dkg_pk_default.json` | +| `dkg_share_decryption_default.json` | +| `dkg_share_encryption_default.json` | +| `dkg_sk_share_computation_default.json` | +| `threshold_decrypted_shares_aggregation_default.json` | +| `threshold_pk_aggregation_default.json` | +| `threshold_pk_generation_default.json` | +| `threshold_share_decryption_default.json` | +| `threshold_user_data_encryption_ct0_default.json` | +| `threshold_user_data_encryption_ct1_default.json` | + +## Notes + +- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is + effectively 0. diff --git a/circuits/benchmarks/results_secure/crisp_verify_gas.json b/circuits/benchmarks/results_secure/crisp_verify_gas.json deleted file mode 100644 index 6bf35d13a..000000000 --- a/circuits/benchmarks/results_secure/crisp_verify_gas.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "verify_gas": { - "dkg": 3042688, - "user": 2972893, - "dec": 3553795 - }, - "source": "folded_proof_export_plus_crisp_verify_test", - "artifact_sizes_bytes": { - "dkg": { - "proof": 10944, - "public_inputs": 480 - }, - "dec": { - "proof": 10944, - "public_inputs": 3552 - } - }, - "calldata_gas": { - "dkg": { - "proof": 169992, - "public_inputs": 6168, - "total": 176160 - }, - "dec": { - "proof": 169944, - "public_inputs": 17316, - "total": 187260 - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.614281708, "runs": 3, "total_seconds": 1.842845126 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 2.121472944, "runs": 3, "total_seconds": 6.364418832 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 1.95707425, "runs": 1, "total_seconds": 1.95707425 }, - { "name": "GenEsiSss", "avg_seconds": 0.758238652, "runs": 3, "total_seconds": 2.274715957 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 1.242666944, "runs": 3, "total_seconds": 3.728000834 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 18.979502958, "runs": 1, "total_seconds": 18.979502958 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 48.341644417, "runs": 1, "total_seconds": 48.341644417 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.006914333, "runs": 1, "total_seconds": 20.006914333 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 30.277848645, "runs": 6, "total_seconds": 181.667091874 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 78.310650236, "runs": 3, "total_seconds": 234.931950708 }, - { "name": "ZkPkAggregation", "avg_seconds": 49.050973916, "runs": 1, "total_seconds": 49.050973916 }, - { "name": "ZkPkBfv", "avg_seconds": 3.850818819, "runs": 3, "total_seconds": 11.552456458 }, - { "name": "ZkPkGeneration", "avg_seconds": 66.056590278, "runs": 3, "total_seconds": 198.169770834 }, - { "name": "ZkShareComputation", "avg_seconds": 52.534038875, "runs": 6, "total_seconds": 315.204233251 }, - { "name": "ZkShareEncryption", "avg_seconds": 114.608395854, "runs": 36, "total_seconds": 4125.90225075 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 251.230740403, "runs": 3, "total_seconds": 753.69222121 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.093863888, "runs": 3, "total_seconds": 0.281591666 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.264344016, "runs": 5, "total_seconds": 1.321720083 } - ], - "operation_timings_total_seconds": 5975.269377457, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.273315 }, - { "label": "Committee Setup Completed", "seconds": 20.279577333 }, - { "label": "Committee Finalization Complete", "seconds": 0.007173125 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 5158.12970425 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 5165.210807791 }, - { "label": "Application CT Gen", "seconds": 7.707659625 }, - { "label": "Running FHE Application", "seconds": 0.0710595 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 835.140242834 }, - { "label": "Entire Test", "seconds": 6031.697821958 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000821730b5f6c7306ca00000000000000000000000000000000000000000000000027d5a4288c59517c0000000000000000000000000000000000000000000000061a32d74244b4e4cd0000000000000000000000000000000000000000000000000002752ab748caed000000000000000000000000000000000000000000000005a4f0140d4a1c7f7300000000000000000000000000000000000000000000000512ac80c5fa266d0800000000000000000000000000000000000000000000000d400822869689e0ec0000000000000000000000000000000000000000000000000000a3ecc527c1e200000000000000000000000000000000000000000000000564b7881fe1989f5500000000000000000000000000000000000000000000000674ecde0680d8bf4900000000000000000000000000000000000000000000000584cd67f303aef6c30000000000000000000000000000000000000000000000000000623ff53a7e1000000000000000000000000000000000000000000000000b27d89433be674a5400000000000000000000000000000000000000000000000d03b476bc4cac62c900000000000000000000000000000000000000000000000d562dd63dce28dce600000000000000000000000000000000000000000000000000007014317d4d7c2bdca3a8f64dcf8eb8b35863e69e18fd4f152ed73eba57c25990e80b7c74b79013d6c8d4d7310cd110d6e43bb18fc56336d2e4da3740b50e8523d5f9594bcdd812036507369a09143ca35760ebd0173b2c22e17b4dc2980119e4fbf1ed9cf2a9021fe43ba1b07e24480cdba82eac2af02f2e755eb1b42e866b12ef874db8f56729f0b56939196bb391b63f552db3f3c7ec39a1d778f2686df8f01eacb03019c80d8bbf928d3be121e24970b738be38dd033d077d66095a42d4f1494b44eb7df0074e45e1376a363bcf636637b442460155ece719bb40d1fc476322ceb66ab5261818431b676c3ee34900e20139f2ce04bedc6ee159747b931b42f2b22b3d7198079fe5ff134d61e1d32094cc83972db87162171f68aff7e77845fde2854f3b9a0d21b74b20d8c2ec809d9db9e189051c0dd8557ec1661fa1a1b0ce322590aecc23e610a0c95bf8e2f1ec67fad3b50a0d2e04a267e7844e7e78b710b17602724a2b1fd0daf5aaae67ae6e02faeb646dbe547f0c699e2cb1379f50d28f36ba218a1b615a728d5b46c9da5f17803b7b0ddfaa6af8a8d6d945602cfcfdb1a8c2845e10ef52b08e4afe6f9717aa1865a5df615627ece311d2bfccb7831e0631a7271a08ce5712e16458da4091348c134bbbc0f6c546f14c8d9c231ab7975bccb9e02e0c991572aba8f415ac14e2e466eb01533edbb3a35795beea6327e635c6b103740f2b82ceef34e542ab504d3202e68b230920bc61d94d1cf18cde724d06e23a482b31011ca389f49559a044f8bc99e2eceffadc140cae4937d763106987d7fe30299aea525ef4ba58a7d10fddd62430590152bdd9c1fe3f5dd865bb2ce4e22a65086dd1e3b14c653011e7a3c89c4abaa1ed50be316fd87f42661de510e98dc9bf0dddcf46263bc755e1f5bad01ba6f03708b0bb47ea5aad02d6a9052b1fce2f05226a3e2e74566f10fe938081e85af521d530facec93e917a64abe8a781e246752f984676b56b5a9c17fb37ec862c78c596c128c75422712251c9e25f0fa4352a2a7bf93147adfa3937ec9733a4e035e4a297fbb1e9599d1de9a59b617dc808f117e08e894d7ebdc106e7f065fa3a096cecca51bf047ded0439a19e86d1f4a358099e717b59523313b12586636c72a0028ee88c7cbf053fbe465dc0d546b698cf132800fbde649562ed1d1c65b70ad4ba7497d90bbaa8dab5316435f6473614f501bf945798b6e787cba40d1202870cc74db20387287ff9fffb4cdf7a0160ea04251adcb89e4bed874a70342a96386f8ab23147019c7e3dc8a6f2d0f6ee77ea1b1683337f4dbdcb041c320a5df0f41519161ce81427ee42e7e55b381564cef1ff1036c245bd9b7f0292937e19a58c6f917d8bc00ffb2c098590ffa91e35aa88bf0f29c027c75a7685fecf83028b01891bf2582da15f30dbdadf169772056758b112d80f494136784c5e55d2ffd210cab43b12b3b22efff7fcd159f532727da6ec05f1b98a8978a14a941d2489d0948799285fecc08732988a56e66e352362a6be2cc8b3127ad5279b02a55f57f785b7e1db4de806714cb6cfc421b1af185f408d2beec04fcf9b045a9fe4911bab199445b8cabc822f63eda13db7ea709458a02c27135db9d0e4ba34442524e335b54395b3c5d1d19d856c4a813e0fc2303a45b50eb4e75e5e2bb32813c7346b9c9c1c0ef2b8cccfde4c1c8f4370c89efceb6be328a05d97d39aed2e4cc7d7536b34af28247ede4000fcc7b428147375abfdfb5516e986f5197eb977a865b855e9772fff94c52ffdf99286ee66fc4bd6ff9fa6540e0da9efe06b9e2cb8c70f7bed562275ad9485849152717537abaa41b09aef482b701578723527aa01c63295dddd454473177bfd0415c06d3d4e1848d50a58252bf4553e8afc869321e3d7796ba736c8b389b852bfec26444709e9d423192bd117d4a7cb64bb293e7c7033de3dc675267289c8ba6fcc0ca6ce0b9050e79e4e2d12956851fb28b58225eb8c0ebc9a45158c59a5655fdde45136ce3888dd460ddb15888a69073d825555fcd88eb1a3d7f67a4ebd571c54d2d1274518f65805c2382c12086776a4e11771b7d00b480fb1c5a78c300cd815f8039dc45ea68e61c51e130a50b94972ea631e451d8eb55ce9868d50b4e67952699e020fe4a972b44a36115a8998e760ca80bfb4b64576d97e16bf0d1c9b703a106482cf6872973e94520ce2037c03b5c1cf4892fe2c779650c50d7b88b19a1f6f4be7f5c78fb11783571256a19df3f4b96c9fce648218195e60d1c2e4c662d66ccb18a71bd0a9fb8a8b23c3faf51f7aef24669b70e1a4274abc0f66b911c67f15fd970be027f76d1aab14d557ba7d4f268f95afb5068341ba0900e85cc7d322674c057004d6fa17189123eae8a63d39bfc00d3630a696781bc7ecdc4a6b1d7677ca60c64c32d111d3c10f75c8f14135d4faacb046778787802723936036487091e65721e7feaf94a87d10cac1095e8bd1785f16c5e7f0f5fb9fbfb5faa9918ce2a047c7c0debec9fd5f2c41c01fe8b3062e6fb6e6961c09ab4db1da10ab612f4c5a7328a5209dba00950b62695a4af7c7304b3eacd7d23b4de36c705a55605659d07d7bf79a382923d82ab46fbaff077b68f5b592be6cac56d52f95b7f67123c6ef30cce46bb673b92b0ad6bccfde38ae2deb094a64332f2645f4cde17c0a64aa6cf9b79a7ed828fee42cca7f36d7f6b946dfd1e01db6832cb6f4eab7ea793718651913922d4f360d4b21f257a41c05813fdec7e7d01cd94552d86eae1bced28135690ba7c02f94c4102e5902efa5fcb489bba7b608b977362ed8ef889df360bb12d3f6fa737cc23ea410e2c92ffcb685b442e3884384753e48b3af21056bb1eb92dd975c59aa54ed01161b2d1f8b8e48050ebebc4226dad4c4efd3ccba5c481e80a25e903ed9bdd7992355c18cd5fab093b281f89fbcf4c49a728b3339e0a50b7c8f3eaf38c0a45a241cd2d8c6780c99d0506d6db1d8b009aac9b761cb438e732bebc6c473618f58a526589895742bb0bc9bf2157b0ac83056acd061226b5155c952999f617205e6231dd54dfe603fac9886d0279e7a02a79d39fc16d9590496af4854499544f0d61623b4c14179daa63917368e6315d7c8044998be25f1fff60ff99cebeaf702094322d12ecbd646a3e725894e99bfa905064f065a4bb9ecf069150adf4f6f234c7203c810c6f79c786b859baf6db1ce8395ded182bfce2fd1385b1fae68ce21305026844979fc3f6f15e3f01a641e070dac977d4c4e8251de5b25aeba717b43bcd322a34d4d3c3c7f307f3cbea1f6c4ff7736b1fcbca1b856a1d39a1de2a09e2ff306d5df092ac06ba92c3a7a18fa39a0a90e5b2c1d4d8645f0ffca3717f752afc42eb754439548876e6b43c3a5d94e1bf5abf0e7f9b5a6686050cbba01a352ecc21598da0ad02780190cf24cdcbc430d5aedfc4770abe9c6863cfeef593d95d28b1b050913d0e34830cae0602973282d94fec6205ec8756b4d696623c67d33e55e05d1cce77fdac4c7fdcfaee946a7baed22eb73fb1af5cea27644c13e2e249b6a0fce2f7340ef3aa4e2fa39063e6c8ed748a9d29f33a38f4b50e12d60c03433b81a80e41a565ac3c7013a1e851151847f3bb22ebefd9ff335ff703efe6189df8611dd23fff4444e25494a43eddb53fd61199cd4c1c04def81d6f77187f8f5e3da1a1129ba1faa492ccfa688a8fab7566278f412deabe1f69458de9a4991000314172a67e9eec385ebcc15e24e8cd28e117a2e885e418a5eff75fa6e0af53d4782282f17a9691bb7192b5b670ab7a577b331366962dd0a36346f28b8f56ec241ea0b6552eb0d11d855088e95a118fd271c8a3a1922ea0bfaf0afe9ea5c758e20492391a1721c25d590aeee4199f47b8e5c32ffea485cde82f7392674c9de1aeb3c183f9916e9789575f1b0b461ed9905831051ce8b19952d6c331efbf48b7dbf4614a5b15d0e87af9e76b285fd9ecce3107a983c726ae5d3d6ec6e2baeb34a9e390ee6e7510fc9579db80f9d8ac57e1cc886eaa4d2e4afe8430ff03b6c6f3aec5b2ebfab036e1b2efe026beca2aaeca416a017afbbf80ccbb2e72791f9cdc6f6b505b1dae159a08ee04351f5f2f240edbe45973e734d98f6f4cc9e2b7da8da6a110f7e97eed170300d1f8541ad1e1cb3bf6b3863a4cb490cb5c39e6d4faae2f6ea0082ec6630d36696ea41f90d0d64da3c8ff4facd1cec120e8736509315d426770883f14157bd101f004e3e63407103842843b512eeef44a77743bcefe483286303ab560fa84c14a40002cd93f22ece518b999cb3121c7e1df64eca2069230193028e8d3507fd7d7be0e586300d994405ce6dd9895c747b49f51f20d494c916c808ec85a1e118a4ba5f6448c66d7ebbbe036e81080e7fb9d38cd1ac591d5667dd060cdc04db6517473b7814ad9d757fca4904ebcbc818ca1f22ecf674dcc0c8ec1bb2763dba05f118fcf7500e370108e9004dc46a567294751b7ca51e51229b22220d0d21392e57177d6e2671cf3e954eceaca2cf87918287bfc1f6bf533708943011f277db9e4f9d9ce32ae3f3a1a3343d87d3f95b52d768e4848a92aff7dfed1aead60fddbfb11590d84445c4e75305bb2313c20df2f95eab7e64eb085ee372282e3acefad52cb23bf8855d760a5ded264e7cf806f650248c6db9c54b48e80709642a094184762433ca772b01c277a6fbf700c2046fac1e7c45852dc4c127ec0307dde473f72467a910331f49b44d665488a4c88895342608a78ef07cead2c305ad0fb767e03b49b8e665584ecbb8651dcafc1b5c116b33ba927fa8d15b98f5214a6f4c52b2e56646a8d9cefa2258846c3de653e2297a4051385c199d278a5926a6a8f3f0356900b0f226530e88bb4714c63d444b3dd57e11227770c75d1d2f225236f50e50cf07d29626531dcab3db32b7c1a3af3e43febe274500247334660014e70f3347b1f65c35c55630d4db54848774e2c140eb7c7dcba10652028cc818c7a328564594c77c3ca4d6cf9b0ce46a65cd4cf70a6820eabf65f863291f0f0c89ae9929974dae20ebeb9d518dd20b2795765cf0b0addfc027806a744965d41d9fe3385a89d328e2f8a5dd616160cda986b7d7ab9ec9543d99fd0f70ca38771d00e76228c39527af15a012f227bff39d231a2682a9bd9c4b45ca1fc2b260aa250577f8fa880edd4a07dd8115511301135921bbc68b53bd2375c7635fd9dee70dfc0e14e099d417eb4d6035da9ff7a9f647f84df2437aa344deb5737717058d0fff3c23d38e8d2548ee3ca6dfb23b0c4dfd22cc50c86134291cda45e0131e1917765092935098bb1a016aad380ecf5edc52dd0f95e566d538900955bcacc4342d16f512ab3e6aac573cb9a03082605ae8a4850e455b5d1641c03a37b0cf6ff90215793fd21ee0fc330133beaa6de3b5d59c40755ce8d0cd2c434b9a228fb79815ac089a2735a487f497d5758fa303de789f3ae7a09298147bea45a9c69ce19e2d058fefcd8ca169e2ba7760c96e7af51de2a28cde2d2174ad0a910dbb954f6e2fab7246831353872aec29ee4df899bca2eb5001464c97b1cce8c138fcc94ec40c8e76d15d58b5353e6b3f9ffc5b871ea1d2cc1fc1ab411671eb7d4f80c28b051dc71f34aaceee78f29a56e4529ca51c856472af3c41d587c25a3bb5ddd190062840ac3eb0fe54d3b1f28f63b89977405cd88ca9c73df613c7aaadc6965661c70747b5fc78ce643b6811e78b86b7f68736e2b356a934001d2ea264a89cfbe99a12dc049b6f9ec05d04083fd34b5647bd585f1be04af90aec97ea1e1ee9d42b841d7f31c6fd3853b6c12510c173a710adacac5b23fee0d5586900c83d9711955a1d5d41d21d27fee28b4989409d645d05feda97d3b12b1bcf31e156c75bed1d3d299392602de76f62b9103cce850a2b5a875ef961bd2f58321bb605928c80a122147f4095ff4fc0c4772cc9eb0052f677550724a61d0bf0d328fc6116e13e20ba1a7ab9f9f8506aaf4d953b7c13ada2220f7f6f4695fffeb8a712b5cac9f185ad15a710203071669acc41cfcb817ac9aa6d2ed7c763d9de8b8c7ac7374b0896db120a5f2e718e228bd07965ea30f302417d9f9ab3e01991d4090fd2b0920194ab038128867db1f90f4a79b4586f7602b80324cbacb580b34263c724430b9b91632d8d497956dc8231e14a4d8a202676f3b8a272bb1e26122f025c4f8f78d5c72c21b998aebd00ed34be2d76e4a43006fe81588d6b23eb5bf4c3367285c9a9ef2927a7877682d3af2d5e33c05808479f4521b3a17168e6c97bdbddb7770f93d2f01b08b7b99d331e78b71e84cb19252fb9f9f1ac65e844728db5722e55ab95b9f41edcecb4f31deb949896cd3db5cd0dc2ef15e4ba0eadb9bbe2657b321e467ae20c88064c8c15e19067e28939f8ee13304d7283a98fb49625609483ae02f2a30d160db7d5914120348062db5917327da32d027a758811aa7fd1c21b6a1dd8c75e09d22df3f7027626e95fcb651e8b7559471fcb5ffb9162d550aa82338c51cb0f02c653bb8a2b59c2959f293a4973748870484c05d4ff511fa666604692f73cbe282d4e0e243d6d9ba21e950ac205857f6d41ba6073034f315ef27929d5dba83205f192e83a1e5003de8d1c38c6dcfa2593bba4898090ff610b0890515f9bd44d06d0e8c2d93df6d36626472b3ceba0994cc76cafb660d6a692e7bf0559db1ed02fc0cb1865d7d25f89fb247bfa0e1a3fdae33f97abf5ae7d9e35033050445f73244d228055862735caebee5db8a119a9d8d11a7035555ada83e299f3387abf5f0000dea04c2732cab2470e3e04688d9f1dedd763e036634d28ba9f8e2c5240ca271898be20fb585b49b60139770919a34305e52be70f0d50192770fc02517f652e1e7ce1906dc26a8fbde6939f69b2c0645b8cb4a3954ad2b43918305a27046603a85deac0c770c7e523137f35e3000a0bf96b8c329525f65957437b757c262a2bc6a9c8527c1359d8e598afc781fc2e7ed7501a730554c58d2a552329f99d5c2635fd5fa08d5535ca03c6ea08662a9c52fbc5959b1a998374ed3f624bab2b5b2c33c8f5aeb856586abd239d13286104a2e90e79983c92ed579bfd5576668f7214523b4f53f4bee84986ad86798ec100d603f863f155fdfd2fd2cd72f5676a852c49103b1c0c67f55c4af21fafed6d1d9ed79f863c9807eb055b1777945f8d4f1e0db4f8234739e50bb75bc8fde5af7ab0688ff97d67209fce8a9b39e7b4d05d25548e0d72bccaffb3b604d3b33ce22039ed319792b1fa674d396a6c9786dcac294231be2234a234be2524f2f7dae91c55e58074b0503b24530a0c6e6297f19b0e695dacbd609ce9ca7cc5b2511ba837f4eca002f7102967d35389efb30000cb181f2a3daffd5d7f9915bf8dd1de5d4e7659638e9584d13714469a9ba5d37d7313784f05375e9c5e34ef8219292be23da93ad73869d0bc71516e4393f5a782062975871276d39f2f41d36b81ebbc221648befd2dd8b9ae5610fabeaf54f1947f17dbe4490a05d99f3ac74e0d537673afb9d993294d2aed3e7e293ea0d280ceb019e6f0704f9b34dd1a8da3dc616c365e09b09075d2fef5b1ede4a3bba6df095b1390ce5a31053ffdb83cb05b9209d37592156cb464012744e90748e708378d9c04416b2fd032528cc00e516f1fa6bb6bead7b0d0a69d97e0a14e3b40ada5065b101efc0f1f4cd612b723e18127513fa2b82d5f5cdeea70c60581191b3c7cd81f1cf121cc958060e951b4e6c76f2d557bdddd63f9f51676352d04e0174aa85a900b4a92fc1ddfd06e8006f1d78401e5f7390ace2e425bed97aec36ff7e00d2f6525871edfb56c38063860c618feb09c8748f76f88ea04b6ed77e5421025254d1b2ed05884ee40dd3e9536f678676913dc230188ced021bcb986b6b18b475fe0ff25ca62e1660243f9d9b164881c2398ee00b789bc8c4444d3682420cb869165b30e224323381cb5cc73620a7a96119458441256a6229f2bd621cb86ecc29165db17aeae0846231e6b67942cec4406b326de3bf6da88d906298be18f50763420171db37eb6250875d716390e1399175ee4eec8b0bd26efb179532d87783746b5521a5304ec07e9daa765361558c3187a2073c503c3981ea7992d9c60bcf15ed2832f492d65ba407662a090ab3ef91cfcdf450a588b143f4cbf6fa16010ea41310c305c41056c1e5d407b1eeff592b7fd4f7321aa7e2be12587c37faba2dae3b5050e6c1530a67f22db6c8febe5f34295ef550fc8bbc338a40623dc6065c708338604f699048cd976fdb44878c7b1059cd85f4b91b964b8df3bf9c70f3c2befd9322551cd9f2b50fe2d058916efe06f883fde51721cc8523939a3350133f533f1810524dd023854b3ae2592e3c34de948a860ca68eab2e7515f901ba4ef130450802767ddf18f04412c57ea96eb43689d7148daf50f18621a2a2f4aa76ff98011bb12d62e69c1c4d4a8baae33a8117b84857e317f8562da97c5769136574dccc90706388d13056cff5c7444a3beacfb4110d60cb4a41e70cfcc8de69c72a693c3c22cc4374fcd2cdef089cdfe6a0c2a0e8f8acf27d8e36b650e13a8d51308a25a6829c36bb3cf8ad207df79717b3a3fc45a0dd3b5db984b8293dbaf91f785fe0fcc0c2b38ec1232d147cc42ac3808a34322fbb5f8306303cffb36614394275526e41f473251fd02c680ab57b99de93a2ef421ab91e4426b4c99f3c6d03504de269b275d2cf9faa91d300dc7bbdb3dda6da71347133816be7b051d48a33903efbf4407f90419f1332cc03e9fd8c41e24a26c8f42481b2e9fed3327ce1798263434e51fb358e785403ba67331bfb0f3133ead7225bd117acbf86d18a8703e1d3ebaef2281f87c90eee0cdd19c503c9e39020cf0a6f0793d93dfc23d0167592855aa6516320c766eb5089fe5089ff9c2a5f43622cca3fa745cf3e61a9ace7b7e1c1efe1bdee1c9bf8561b342a2cacb2584c6ae87cbc23ca7f21561483dc48e65ed74d70e36d273946c8af8fbfac07b8067c7537725fb15dd74d6fd8e06d9560d04fe9e0bb9cc66a552723894d500bcc67d9dbbbb2f7def521d214cde49257da5ebba58127ac3ddcf681962da25f22c4452c92bb8643d2d51cd72af07b36ec40933aded2a7ca13c7f42fad892ec2a215c0b050a497585d5acbef59321362e9ebc54f7d7299414c10a9a83fc9beb917a3272cbfffa4e7da3fe84a0f10dabec1f7e5a897e0962a0e842037b4604c054cd61aac3a07fc1c81f3a72e51e2ec990d4db00b9eb14f32933b1cbd476385d2d315eb05da63cf9aa7392e86f8789f1eecdc9aae8fc14541b5d0cf84ceb4c04ef83b692a470b986b75ae8fbd52ed926f7c50a3f0a5c0025385e05933854303b5fdd3eb968d912173130bf0b081ba8ac3f93cdefc9a8143fa61d6377db816b1367c023307d2fab251d5ca6e846a272a4577a7fc82d0901acf57ea102113af63c159c1f5d0d91078ffe6f22541bdec846436cb3729c8126556cf495ef535047d5bdf6cdb5aff1c08ace3e8ba81eecaaee4bf99b80bf98026d93bd1b87cf7eb21a4085647627e2b0b2504884d459721c7b979153b14d161ecf00bf75bf12b4c944c0096897bc8c53526c746135500931ec160fb77114a317c0ab237646eff1345dd26ad855f8d7f7228ad671ffa4793f582b208bcd2dbb018fafa828663cf93e8ebac379ed326fcd502f1b27bffe1912716dec34b4189d2949271b39f8384eaaa45e64fcc800b5f805dcfe8e9986c26d7541bf962d1166044483913e751c75bae08db36edb57973712fba0c80d6813f1e051c2af60ef6e117a84999eb5ede01635927eed50f201635f19e297f45c406e75470fbfe8a40f0db953affc8f61b592b5b0bbf515eede750bc8a0593edcaf5959a5445a1ae0c1130526a514a02445e3a6978f1849f50a7c23db9340d42577892f4d385814e49b151466ff20534e74439acf791d130b0f765f9c9b2ab66fda9098c7771834d71e0ddad7d678af5183635414a7ee104cf52afc1dd2ce8fdff5ba21534f4d73667501ddc7df289a79ef1a336f930cdbe947883fab991b26adf61eb7837d582b8e9d15ec335ab0809126826fb09abb2da87e9aedca01dcf1a662b74cc555fab8b65c013fbe0dff90977f2a74360eb7fb0e75c59408389757e17ad071d76c112ffbc4014b0ca129f864a2489339ed260cf0796380943a2618f3b13e66649fb04104ff15197fafa3b047eff947a782bea063ee95bc5b8cddbc7db60661db0e6a47be4619fe21e5918752a3f48836a6630ac27392a10fe2b6a6b292903709ee2f82c75e0d667d9b51d5ce9ff23fd6c7f2c5a87f087748824f9bb170c33a51a704be5f042a94345fd5fa26e80dcc485aa26fa6c6061d6cafe422caf1eac52f8405a6c0aa2bff08666e2efbca4f1baed7f5532cb579708baeb3555ae6b984fab00610901e1ba452a848424b71e55829122932331f5306b62a5beb24c70237eb9c0e0207591d783524c1a28c1fe61f050b6bc6c300011f151959e387c0125b787cdbd2c7241f837fb79f84889b30cc8e5e84cb49a895585e78ca301052c5df2a4fc873db770367667202002946d7dfbf953f113a006ad4837ea696baa9272c58c9af1d6d8e1f0f21da94147cbaf178fb414d0a77e22c33c3fda3d0be7ebcfb9d95daed750008c9a1d8ddc601df5867b8277024e8c2f447a739164cc63ea44c03ad55282f181cf08f3933494d3938e6937267d1f54289a4e1f19d16abd449ee8372fce0834b212bfd7c33276b09f5ad9221383f3bea85ff25427d5deb17e7a510d4104e32aa160ae05861837210115dca42c986c81002ca19b8d3645d1b473134a160d34ee3261287fd51ed3dd9518c3ca8a42d99bc095a056a3a51411fe822eed25ac7b9392e319e63cfbdfa8c25b357407e9f6e2e249f9a75b5fc17b8940b10c7a8bdc0c80ce7c78c5a7da26bef638318da682b033778c232ce708d33e025782dd268861c06d6ea5cd80d15066128b7b0b5d5b6f51f240ea176d0580ef7b02eee47a5cce712042e07964c9c052cedc831ad39cb143a888041c8aeca71ddda00e103d609160b7ed571aa0f7dc432de1024a9572b2f46029fe8563ff2fe6046cfbc4589099a139ce636ae588318153e65aeb57232762e7be53e82ec3c6f46ee4f923625378f183f5c778c1d9420a1523b518c7b970ba291de12f4ee0d3abc0daa36f7faf05901731bb21c7b0c1e3fa46a76e005632f8eba62e811233000cd2539f44b06619c0391e67d0b33b06bdfc98775458a4293c9515b87e5c25f6cb6d29918837096890ed9428f0cbbdcb8eb0edc385318bb3e61aebb5774cb91852ccceceae70c38cc2056a75e1d63ae67c774a818ad111bf39acba607cbe4864fe74d45aaee92912e237f27cb4f8d40f7029eca2bae4caef946b9549348e37161a6e56168328e03490e955c18c7bf6cb2ed0f8a4c94bc635ba90724e62b29de6d7fcb2249d3951b32012abdacd304147bf0b87a684c56a84e4654a1fdb1c2109d4210f7e9031f3ff9280148dad4fe9dac49cc6815a9d2910e9676cb332990e6564537f5a787e126bb154cfff073cfacd39e74a57c4d0d382155eea6093b96ee0e0bc1d204f58742131e755596903ab9eeb3e947677bf81b62c73f504acb1adc7e4dee30c034ab75790ebae3861e8c094437ac0d5cfefa3b3b0dd75c18b443e46060bb69f122660a7f0d8147ccd093f2aa6b1ad705f3791229891faa6f27122529f42e149eae990fde118d8dd0656f0fa9f5267beb6ccc40fdbbcbf34de723c173979387831ba95cc811e1c79446cb200b31e31f3200a287f582be616f920f3b24590529f1abea7c180a6ce5ab8515f5498960440ddf5000079fa233245d1df8fd688ade85739eda4906703d70e7215b65746facfcfe1aa1ab8c7c817b137661d535ba08aa63aaf44f2b87690dc15322b2c5cb26d729a557bf30748c7dc60c36a9610c9e758330ee4e1efec3393675cfce7c64da0c1fda8ecd235b8c76e3681cff99daf8473c686d1f0bf6aabadc7acc20b88f11869b498b3d839f679d443073ce6ef84512ebed8821286504bf8db57ddaa7f07b0ef748604de3498c5a8be6c3b5b8121515bb8306782ca30bf541db60bd1eaa09ed005970e3c80a2c7c143f6504d10b208ca352b8880e915045e50f5ae5d45f7401b0f70076e20ca6635e89bd950b832ff0aaf0236e206fc268a7f893c058c73030c70f4c7deb51ed388a3bee3aa16534d5319836be14f0addc9788e212172376c5f8cfae54ee2cd8792ae628061ce47cc1a22869761305fc7a5369bbe86b6aacfeb72427f397e40e8b901f5b3e292d7d29160a11bb05979b806f46ca206ada7f1ac05c54c4f49cfadcc39cba3e49e3ccc263ff77c319ceb2c976e8f0ed37f6a73c371cfd76d1b82af0a1e58a6f8e6c0068076db9c229f59e929d9547d2de2db72aa9b89fd37dc41561ab1809614d396d06eb5689f00b058cb17c2b3cf46fc636066b4632a5023dc55731dcc76db3dc1a0a0ec84c900a469a7d17720805a3c2bc7ff493a2df926d49a394a7a0c45c7620f051b2ef14160d19492657bb67d1bef596d29b79d29a4babaa5428795879ef66d3aa7dfd7807592edaf9d568afc173e452e112179d987725eeb41f1182a9f2bf32db42b63b1898abae8d596b33efe7b6607af0079dac5d356bd7cf13d10d0a4e0768f2114c25ce2889ce6c34db05ff06fbfa4b955b018546e27cd0bbfb2bc0cffa0792cb5823bab8a977dfd68e70f068ad03ee38aae0e7d49a8957c3878d643e6d0f65fcec272c725699c16e846c8638f5cec0fa2d981165b3bcaa7602938f4a2149ed71e10cc23a9c5e7d0a3f5461709b23cca9f4eef68bfd06a6fd5e505e756ab4c40a8e2859e0f20337582b5f4556a47c503934f1b8ce4ba7b27a31560d3809029ea2d2067381928499d6943b040cef2cfc40f7025ab51d66d46db1eaf2dba55ffb9d472f04933ae5c2edea96a0468675bdd6c86d3446009068ea089303a1b9d053a8841d98c5b5a491cc1f8e8e260a3655fd3a6b3dbc20beb8e874272a67260b49afb0213e712579743fb62f8ac169ab6500175cc164c2a5caccaa675159759f708016141c4e101152c0e8e55842f9bad78fa3d8c4ed129f91d6450d6c06e85338347f0f6a91a32ba22a390cff1ceb374f5e2b6745e8905f0866029a4c2f9ea7299cf31ea5b0b47ee4d6329b1ba0f1ed46fb0be938a45a2db24560edd0dafb2f1d5526073075b6b2bb62555d44907340a599754fb868566a768b458d6f6b038866a2512c37b7d743fe40490af6e2267fa03f03e2a8f9b6d95650221eaf0b4c87ccf6d505125e1dcd88c3be809632f750178fb8b6bae40b12c686a256feeb481b20ec7a2024b45fd5c00f5608c9cca96508b2425fa584455c1c79dea9c947c13e54f2bd0b77f70ae6e1aadb54cd3af5dcbd773c96a36c60a6e94d41f051284397b36e4102431a5c1227d7a2e5eb432dcc11f0fd5884c17eecfe66a1ce2a22b0419b751d117401dabda490ef21888c13c136e09941774ff0b070e9b24e762ea7ff7684822e8d26105cc2ca0b833708f06574954f6b582e8053824a60dbf179e50db6f55c069244550b03c11e5cd22653891fe5064198a330115698a1d852f57c32eb855423c9db7c5aa271f155577ae09f6e5a7e32c2aff1f5150feba43b795cc2584c07001cb655353eec53d999060ef4380640747f65e57196198444330db72f8081f5202a4f8e4f598610629f96fdfd5734ff55a7f4031c8538f9fe80d1dfb973e8d61beb98f1f854d9783b319368ae6f9a015484bbad829d30cca00981f840ec563a2e24c11cfd9977d1f787fb0a395ba4b1acfdbf6f2bafee57074e5328affeb76204ba93ee5b82ed1dded622a843db90a4d19c0b4ef46867161b3f6525493b3a3e285408073fd97e2f53fdb86c06e9845e8d410462857a2b9243b348ad17b662cf06686483db4b559d400f33726b232420a522d3a4a63376d60b56f5ee7cac6688203272656b35c2a109ed301e8a298fb397d7b66d22b4867b9806269b28a5c16d2c366e20f6c78ecef995bf41e82d6fc7cc578e293bd917b60d9bd6d1dedd21bd12b872b9d3e443055b534b753d81d39377c29f810995cf6238b69779a358fb6208eadcd48848b22b262ad65f2682172834b865f293d2fc5cc95538f242b21cd12261023dcc253992cbc1c548d996c675b14dac9b530fcc0bf78fb90bad6027e52502201fd2f781fa9dcf4da5c91b6706f2eda965b330521737df12d13d350c2506a779924f2bfd8c634cd73c84811da8534e0b14ee42996d6ed01f879d4d858605adbb356eec9115a7e0a689e0de4c27be333631fcfa9fec8d02744e206dde6110f97142186a44a6f4f220b1295b4e1b0385b0826984a145ac28bcf89a9c3627084ef803b0cae77c7eb29fe2d6ff15e3762928f2f1100f4a8b59adeb81a445e70c54d7b8453eb39ec2e05e612d1c0884eab9b6d8792606e5ae8627d0f4d44beb2d4306f31b6cdc617da0f5374ae79900cd9b9f693debb14d59889d5b802fb8471c0a414faa8abb86d379cf24a883de065656be4205dd39718b1dab3ef93ba2e5107d2c79ae5b564bdaf2971a6ea04febbcf5e2c7b3e3743d8b37631ed2ebfc2a116cc9f8b27037cb4d7bd4078692ed74caa2a1ae3458d1eaef27cf48870bd95a", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c91109676183000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008f26088c208eab68279f6f45f234d271000000000000000000000000000000008ec0f15e98d9058c0e708be73acd2b9d2197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f2a318f2fc85748ea90c49625631654288c77791d8dd0792e201c748ca9863ac417b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e2bad40c7a2fc975f2e4811607e4d5fb72361dd1af1bb2364be799270aab5c6f623db21355d312ad82ff6334e39518d7aec226005c56d124daf506a44749cf370" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000006443e4dd7cafbc6fc000000000000000000000000000000000000000000000009a5b11556957a2fac00000000000000000000000000000000000000000000000481d8c87ec8d9e44400000000000000000000000000000000000000000000000000027e3c69ce5a8800000000000000000000000000000000000000000000000c89919183396d2ee500000000000000000000000000000000000000000000000156b5f6351b4e1677000000000000000000000000000000000000000000000003fa278d2c3f3cc83b000000000000000000000000000000000000000000000000000257547a6577fe00000000000000000000000000000000000000000000000a7acecc082a3669e1000000000000000000000000000000000000000000000004e4ee60a9610ccb0100000000000000000000000000000000000000000000000139a7b0dc64569eb80000000000000000000000000000000000000000000000000002797aa349525b000000000000000000000000000000000000000000000000e4efb10408e5e22300000000000000000000000000000000000000000000000565dff2497b05203c00000000000000000000000000000000000000000000000c3ee606bf31b8fa98000000000000000000000000000000000000000000000000000144da9a140bb701dfc1ca1fdf6f4d4992fcf7bebfab3317fc2caf844b02fb8ac4436d5adea45f18531115019e88ab0ae6f13233c1014519feb8d5364b2b488e9d836182eba9d800f3b9f16b517a82e074902c3c99bf34e7be3eafddb913ebb2c8d05595a159bb2a543541d15947ed4f1c1620e84108ea3ec32503ad7d073162c30b94474e387c0cdcb845177847e28da58f2486d85540c5484ae86f2294cb46b4cb84d0aa3609083d483a8b920f898636ed7798e980729ee4ba163dbecdce8b02670604ec9b920e6c4aa8dca3ce558f7cec8fb7f4bfa1b0d4371b18bc835220613a1409fa080f120c539f947fdfeabe4f2a16449a964098c614d6fc20e3e80d0403f0241f5d8a13147d749da88cd2a0d8266d4380e5070e0fcf87e2e312a3cdb01409afb8ce082549d88678d5f866e68a4d0281b41a6b539a157e18a18aa47a9d801ba82c15b4285ed10e2b52a839fd1fb5a55f8de22855f4af96518bcaccc2dbce494a296ebe26360d30cd1d486f49978b029766d49825f3c54d91bd255e22e279a69caf0742070495855195395e1742793b60d1378c09dc9c7053216512cbd2452209ec4be4071c5a521b696e975b1060278c4f77f59bb6ef505a7223d5245c2faccf2d7a7b17bc6a08396d8036223bd5f0ae7ed2f2891a3c4e8846b79bd36ade7b09fd6627114aa182ae1731eb6b9cf19b8b841c3c2b6d8056888915d6f3833646d375991e22759e6ca02fd6241ed37565eeabe5d1c1ed7734680293580c5485cae983d60911a42e17e7646d7d5bf1b3633d1bf857ea6d0d6dd6ac3947070d93be4e391a921af9366211aa3384aeeca7a871208ff44293b9bc62fc7a31c346b3bc0a41d8ec2faba055c65299d4680c399879ad3b09b91ca75c154614e7c2365c05d9e1e1bf0312970f67ebf20490f3b18aa7c84bfbf44044a62091158a689ffb7eff443a0d006f17dce991bb9008594904f9599b39252e57e6a7ab50656c9e5a8df72b58d51b7c9f4435e6f52a478dad1fa0f66a4117c91477773fef118f1a5b2e2246a83126b004cc496b97eaeb0939d7fe1fe9df4c45268c13f7acd87f8d7a7251397537066d564a275044ff3e9013c7416550403fd3b89f31931a1c9bc4c78890d6039319c12ba5bf473841715e3058c08b58f6c885980ccb7196c78fbab8bfc448f50b23e59de65950bb9e7523d7c99364b96ea7e2b23207d02a56f9a6ba47ffe607b0110784790f007d11c1a16dfc27e06e1ca443bf4740ab87fe60bd2e4719005c8706dac42041fbc232faedc6e10c6a6f75b3fbc206b8f7c1648c1104fa3deecec91ef7ed44600c3779a8af114f9e25af821788f45bd741c89b36e53c6e5e6e983e23f372c640298eb2ca3865a651b2add2714020d6de218a78a66e53d5eb9c037921c9f13da3795f03f8a780a09119c28b795e6e6b97d67250bc77c40a81ca7253285c8d29c99aecf5b4aec5316f45c7934a7c13e79ba5f526153174eac6f0fa7f1d5423429dd2204d5a0749d4f1d72ded1463077de54e2914170e854d559814782791a449f5831160a02643aab224d9ccadbd7c12366cbe5895233711003e08701b69ae17495eb9c96cb4741fa95d3a2342926bdd5a796de050b055178f9e91ba2582ad1dda122e9168349f910fba42f2389be4035b6a4a0776ec380eab4a7e1818da3933777a20728cd32e8aeb3f6dc1c954b5e7ee588996ffb14ff769561b1427200b52b53eb0c2aeeed7d7ddea931eb9dcd23c5652b768cf3fbefce53500b8030754c5c1ca1108f3ed46afb942bfc01b032b26a8ba44e62c0365ca4033b1b219a80f8a2ca3ff9ad952bbfb2493969d7eaa90cc4aadbcbf36dc76a0c848bbed2978369892c56259b660b0a07ee28fba8b592f129d029bb31b80535d8003742403319cbb781f5e4270e2aac914ced8a3fbbaaa3d9f1e834bad9b9f07b44814681c3e7cf0859f312e121eee65169a441b1dadc576870842c917a36a744b4f4da91c33208fd45e9c462ef67ea2f432b555a46ded01dc8ac89fafc61142efa714650817710edcc68e99b475b7ab403a6de405f94656a27d39fe369e561b26a5457c2921ad7d3ef554ad5330d553e322f95d5c40c50efce4a009493e71875eb2182d0f82765a780330127718248a51508491c0d2bd80766fc4007dc818073c337f882d78d25f805d23f12d1db526fca5ffb5be0e54a6ec25e46bbda65b6bf5aec648184761fa18ff033b31c97d42fd3a43ff3f7ecaea4d304d336e0e917a0d2e4425050be6d7fb830cf4562eb87b213a5d6a0dfe7b3d63f8e11e3b1efa9411183811071ad9e3d533211eb93739574879ef787af1314cbf7fb4e874c3ce87ebf8c3f60f93f0b8ef56a6f27811b193c26971d3e773e76e68c5daf56140ba20f67be6242773bd78452b12738a0ec3b6204e93c9377acc0358971d74ac9bfeaf9245d9421a7d20b5a4a2ecd40e4d23d71ca5a7e131b932acc89bfbd6866b38719ce78d212cf028cef1628129d642240f4b2730357494ac27446f5e56c0978e0c2fc239ee112666f0bee00ed47f0164d22a5215d5db82f3ab6cf249bb50703d3dc27d71423028a4f72123edc079901ef7ccb56bef61f6591b0477b23cb07795773f20e2bb12e5e0ef459fea65851cfcc4b387fc42cd5e71bc089a7f4381c913855a4f2f1f0106cae843799f89fa4b279a364f633a37e4b2640c69857e61279dd803ec5c6d039606c9c10584fac7c1d5b10de7b25738d89fc5569329afd79e885fdb953cad2cc05964e713e4c71e4c07ff1cd10c406ff55cbd851532a4839738d7227ed257241f748badd25148e8ca89366948ebeaee84d613987f693e0d213aaa594db3021fe702e566a8ad2d75ae4f812859398669a51eabc475fa34a0d77bb9611932a904ba84084825294979e0ede81171919e8bbab7354cd01123a8d61d9548b311ce05d765d5f5322a49e084b9796079b3196f5a5ef6761db7007dac93649c4a387d02a34ad68acf136903543014ec4b3d4a3fe900211ea22ea18eae5100b319d329034f88cb7f95f41dded0a0ad75b68c71278a61b51c19c035158fbb8259d36bd505735e825c6b8f845e8845155cb93ba122d3e98c3db8da44666609cf7192ff0d15d728de03a78427d9b166b5ad8555e4b65afb3244bd53993eb496c5cd50833d22341df3b6674d72bb8271c4b82e7f03740f15b18a7ce3fc8943f81f826fc8fd227bf5ff41b0989a237271e140ec6ba99a0c2c913033c1dfa00f9613114733a42111810a91eebbb5bea13246518438196d97dabf006219b2203cbaf73568d7f8077e01b07756ea1d188ab0a2d7d2b901634618048e281da916256acd484950b7175c097adab59b620eb4718fc51493f0566c9e30124ed27baae31fcb691fce092acd8a3e8ea251794afe8d35d55ad7037ee89476c111e04610f219a1aee124c22ac4339cb28af586a14d986b328dcbc25636df3bd150a77cea7298745cb7fd6b2ef63e7413dcaf3505f98d6569f1e4c8f3d5082903c2a15cacd73e75fd8df54e2307f9e71217ebbcf22bc61abcb8690a22409214cd7847da52e761f7ced049a31c763c92153a0b2904739905c9cb26452f4f573ea8f9a7740b72e854a50ec596115078a8b11c57010a27a043652ff97f51ca448f30614810e3765b8a4149a63e291d1683a4b9a0b964f6ffece7dfb0cdf1c4ce4f0fc04be99672261f50284503148cb9351ff4662a49ec4803560cfbc9165ad379fc90ce53beb5ab320807a8d91d0a10934a2b9e4363934b89c786d314b1a242e9128fccfa473dd4fa8f8496710aa112f31617661961f5e97fd2c3fea061bbfb54946e7687ec43dec8861421bc140c7c1bdb0a3d9b5909cab572d3434a2431c3236a57baefb073fd1bc6d6d1912532702a84570676a2eb0d0047ac4b2bc9fbe5c927e3dceb3c5c249d3cadcbe929008bdca758870e554bc839114d0ba80cca1fca48d28166f949e52c54e6928b1b9605ffb5f5f6b3db23931f7e59b994d47f7c474a3564aeb179664ee01f91770839111d61a992bd1ce5fe02631dddf3d4ad212e79a3aa20c635f97ea8d6db1a1bcf6ec685335f5a88413279438d62e691fc5848efb92ad7dbf44ea4e6f23a8d20414139bc656a727e0ebe69c8a9dc3802d314028e5a8643622b705ae5ae82e01e2578c9d36a252ed8138a7aacac14849121aaba10a66d6ddde63431e9bae2151950ccdf9487c6874ba926706e38df897c4ca3183893c211c3846182cd2588da2d398a58df3e979a559c498fe06a4a7a0163ee286354b8da07da8b9eef9945c618800d7ddab549c895cc7e2ff3000f1781554884bfeecb44e4124d603ebc19b829730a60168980a8fa287fc8f86c0a8a90ab8f38e71f804bba01d8365d1255930ccb79be29beba86407961ab765e979b3dafc32bb1f393d16702fbc3566aedda089832173ca5a7e5069fad1e0660959d49e0bfefd74d2314b393f2fcccaf682a22bea10fe8179d3ba752e8044853c47f4683b4310914f61e58b7a1df42504a74113e6facddec9a9ae5b220a1d6cf4fc0415e9f725e11104af98a7018d5dbf4b5132e8a07a7b4803847e32293d13af6a33d297cf15e40db4bade0a4774ac86b0b12fbacf70560a28f3f7b4db8fa73364a46bb2eb57964005bd8e58c2e61eefb861e701eadc30caea24196fdfaff46b16dc10ecaea90bfdcda36bd80ec0f006d9d2e1bcc89a79ee552a9d1d32fa60bc21ea126a76871ff91f479eebbf887884f360274da76723b26de1d7a5e8ce9ee53c7bfe8b5f29ec186a407e75ca0e3d661862b867f5fe4b79871eed93603d868ecbefd6a9b1188007d91ab5aa9944fde3f6b18da6194ed89cde701078d634b6c54192dd528fa637977e5c3675f4ada2b502e13be091100049adcf52752f514bf95c3fe99e4e20abd509446b5fc66a8857e1d2001066787b655dfe8931150d5648b382f60bbe72d6e66c73e3e6529d54dcf4811cdcc0314c615042bc66bf5173485c9881e9544493687da1731b6293a9679fb04b9622e9b578c81c7608e01f3053cef3f632d4ef5a58f172e8bf12259f6d5f21e25a74e2fa134a379f6308d57c51335552484fb0e88867c9d243bc181a090cb031f58e0fa9f655929adfa77a05baa4894c7de6a6d37290d3eb16283ff637dbd2fdb3500df9f2418aa9a9f250fde66f091fb6ef5acdb61a27902e22fa6d5fdf0057d28171834d980f73ca8ccbc0b303a5062a068e03e7ecfea78e72f78bf920d00be36994a8ee4f42a36ed3a223fba9826a967f946e75760a85a595015b8af2e24ad7c59e99b87f60a5fc7c76e351614576c43732e32d314ddbb85daa881eeed1b150a822d9945663292ce60728bbac00dc5ba3c649e814005a52df5c0fc59b81011cfe6bed95a7ff1808bf7c6e1984dcfba5975ca1768a00381f2974a32d1151e75c2894ff4581c65de16e354ddf938c056c93a50c61579815a9f0282488c76145951a635f929a71f8e662224c9364e12748d715cbc7bc93c5606f07687844222416ad582df9b9091bb790236a28d090291602b43b80c146036ae1d2d3c911123f37a291933fab7e30deea52324ab9ab3adb486903c9960992b5ad8cf6d89670e608c9b4061890a793116ccb7fbce38857e020824d1db42f14fc651164d22a60aaa8f2ad0f20b63778277b1f641d7fc5d12dae47ddfcb2d55e6b870d8b2814c20ffc73af5fadee89dc3b3cd116e881d5c768520e9fca681290ab5c58ead371022b65ed2116f353e440fdbe5d2fdf3f025e2a6d2e9d3e94c9293ceca8086b0de285a7305750eb30cacef7b5469a0797e42080970c0c3e70f990892b6ebff1044023dd8d11b77a419b3aa19d4bdbfabaac8d553dc042fe6c4555b6a14fd64ec980146ddd60ce32d76980b01b71eedf0bdee8b18f198cc0255d73108f17b509bc13025bafa936bc61579f8c1a9930017d1f0b042c5bb0566743b6c731e05e3bd7d1cef12b035210c3ecf81d2caaa99da32fea59317e383b970e419f861ae2573fd2b3c6b4f0c1ca9dcd2203e4b1be938e7838f970bbbfe78707468e381083605301c190b6c5f06802b1dc8b2280acaab86ac5046231ef1060033cc35ee76fe2baf2d661ab0c19ad8987ab3cbb89c97f3c6368ff099e09d1febe8e7f6ecc1479f972b5c27cc320c875b5f129f3b6ff24b4c8a2eb82af590659ba225f3e190fa4e6b1008897acc6e797f4357055c26eeec55360f23d1da9543b6ca7bb34519652626273e877efaf30df25e18578770476876265c499564d49f34e9f6a51e30b1dabe0c32c7acc142ddde363e9e60696c0fab4def3165b7cf5799816352fd5147cbbd053810b943836eae47e715380acaafecdaa5c56f78ad25cd6beb48b30b71e97224546462e575474e305a384c11241bfb4922692f7ada05ae8ffe3eb5a61f4a432346ea3071cda58fee452fbc4db70cd573ef7d17106659050ca1418bbd2681da0af56fd4cc9e2c6418372609222d7387c272a336e2b34a001899e647750744331b117ae4529b0938e4e4712dc5686b6caa250480220d3e94a35b9632e3a73fcd126572f8c3d4ca698b890cd2cec074e5e7a8a7f6f624f385cd36b9d21144013e1d4d49e7dadd46d3bb09ef8d4358a93c905259d943a18b6a020e9701b77274ef12ca1ec8b6d0d112d3b1dd49f29c7bc13ee5e7ce00c1e5ecce92a8c5b0b5b464094dae99c3561ef02693568d05c04104963444dbfbe0e6cd804ab19753689d2f1254bc80d6a1dde41cae49945131b625cbd58bdcf37f4cf699f10aaa7267e1d62acaedd0bb43e6003a32c7112ec16756df55e6b945e730898cfcbaddc25621cb0f3ea6045ecd0c18e3c46ceeee924bcd8b5931869ec38afe8b79b6dcec6e649f26fe612ab89de39ad9fb9d355f67b6608ea70697d9f7125cfa4831a71b894a0a0bf5eaba34f2e38a421e11ed8e30af9a02bac691e911dfd2c5cc8bab6fdcb23904e79827c342bca3644d0ff0be3bb71ebeddb24e04066ab1b9fbb82705a3266a1b742992abbea7a4b968075d5348ff4e1068b01de9997679713f13c50b558d69224e93ff28adec2874a0f0131ed5937a9a84f1a1d27815ef6d4412936fb2d9ab2c9f461e5eb9aaf0b937abf2c25c62926c3052eebbc13a311af28dc949296b271595a279367d738f840aab4617ae5d46cf23b6a6f6b932ffc79d71daf27bccb2128f76718008bc6be9dcdb3c0b25b5172b1da3752ebfd6f9ea2c8c609d906b690b01c120df01729bd22d73b94dd8afa3b34ecbe74d60ea6d449c60645f1528622ff8a547dac1025fa5fce863dddc98f0b30fad91b413d57072fafb52119e58ca1516eee808b2a74efc1cb43944b1974e17032d21093d286720396ff7399fb63c1f79cd6a75644f67501af7e6d876bc562174d7fe4eb0434c4cfee35ac6be75ac1820791537c5df12a815ba14d92b0f4fd6d78169fa6451cfff0eac6ffe080c5c2bd37fc059beb2f3793a7cff7025761e181b81ddd54b11acb6032db51a7a8ed713315f3711ea9f0a97830ae8b36494b8543ac9b862745c4215db849ec6a8fc6a1c5ee285c997e54b3c9fa212c4eef18b07de6901258f3ccd0dcf9ae10e0cec6028fc22b8bdd77d9f5f5753eb121e13d18332b7ab5e4e1b8da6ed82e9149a8261093fe0c9ae8dea8f167327c5614e10282a690196332af402f400d795d62b79112c1b39336e2d239a9a003fd684b32c1eabe3d12736033faf849ae7b6d13f44770823917fd825b94292e2cc911cc0bd9f55e29597fc6463429b0e3113ce09bf1223150eff80c462c613876c1afb7506910c9d57bcc4294dbf2154e84ded32aee02db67e7e6d6ef0b310397c49c631a159f637d7b54fbba46993bde91e06229569289dd10aabdca78e37e5e65111201ba9db43a5d736d09002d9bb979ba354884f0ddc0e655817c6eca1bf4e8399e6059ce13bae13442905698486b29df39028c226fa402d682da8022ed12079080cfe05a60c2764aa1d7b8ce819ef89b73474a62a32750b401f56656c401f6fbefb83b3fa391cbd494326e63cc172aac33ea37c2ab915cfa8935f6c1b643657122c82828d974eb995919f27f9045f1bdaed542120f563c340c7534d63bd89254d5aeb0962d72358fddf008461ffb28c7ca3331224a274f4a23db46311983cebb2a0c25c643781c581dd1853a98484e7daa303272696b4c3fc3250899de7b73273f0cb8271959eeba9f47d6f27845142bb5a30241b037f5bf9b9a97dc46a8293e61834ce50f783911f3be4c332cebb2a1254d2d02770490657b1d03830ed3ee8deb26801236387b361c6422b4119a60b45c17936281de0179aec58b237faadaec4581354ac0fe01e42e4aa3e7d9660abff18d87106ec366b781a8e7b6815aceeebca6d1742fa5b320e1bbccd556fcab55f5df0fa25613aa330b14f831113d7b0e58ce43bf10ffbb5922d8037fd6e8895bec323a41dfc55b695ae69ae883e77735dc097ad451c8943fcdb85658d42a356aa45a10d079f8b8125a7b007f583ba170dbace2b0baeb316a85c9992f4cbe7463bf97e420595f7e9889b31842f7bcb87ee9e1659e67146d3eb1738237d580ec4057b010007e11de07d72ef853d011846238a94be1d45b6af5221dbbaff5d7f2b345aac312ee1f0ba76c97dc811aaf550748da69a8e703e30d162f6c4d5e3bfb584709c610de2f1e1324166ce349aae1e7925db177f435d4dea9943c13a1a14583bc4806811ec85e76d455b6ecfe19375719b71801e53989ffd31d68393958d5db8d28f5e23b717a3076b3f25ae79b373b4c2f8b74ea54e68d5b611508ca732b86b8e682d0d2fcf22fb341c462a809a437c34275a2b1e99cd2f306ca0e5c471513ca2636b189a2f7b2fd610eef2f1f564c494ccf64b450345ad58e46520438d478c922304080088b7ac282721cda9cfa89dfda2df6b663c9e071514b42a6e3e3d3f4b8a382289a0b58bdcc4d9e19fa811d6cf64b4a703bd072cd660021d4d515e59e6df6b3046f12a2efae22d170874e2650dcef1a24d6974f77cecf09d6fbfd8b34feeb40652708e4d2e7170b5938e06b95d7239537f7ff192bded21492eb0b817d58b4d165b06c7bf692e0321aa3778a74dcadc77994701581806ecddaadf49f7104e7f0bc5921a00718a37a6af9d199e6e7d94a2bf032e92c0eb564adf2a7fc68f24701231b482e813783b1f35ada11c23433530cc7bb6fb050f5e9ecd90b18268a629098693da7c2767302986f142e06ccea3467126f980182235dd26f84f3b4168402ab0ffa139de9f700d94fded6ef3da03e6137e64131ab66dbefb912545cfd9ec2140d43b2b04494a1cb5de412225c593576471f8421345c6461404437cd50bdc05e52284204951c094ac5183937c52eb1f89d5efc2ddad671cfaee747cbe411027d28a630695a70bd84e6c8f2204baa7816303b598333816bd2e069cdf2d900c25394ac8abc092015c03de91897529f88364c5920c96d79d36adaa49fa4ee79a1acf5eb5cc8c68c2d207e2c00a4be93e9f7f68fbcf3b22641cc46af5b00e468b266253abf616d8a7219eebf4ed0c186804c9aa518e6498a95d27870b09e9a7910d7d2897abb953838358a1c632c883fdf1da7f014a0145b925a4d62389afc2d21d320b72198c16c59816aa7980f35282f8161997479712acfcbf2ae7fc939048221d36c6e5386de18e2e31bc9151bf31caf26bedd9d9a294b1582a0ee1fd54da2b29ef6dbc542e0a402324832ab4a568d02661bee1222aca13a8b8fedf6d1a212c033e3b768116702bbd085431c7df39c570843069c428878590357edc235a0d12653312e04bbf99f98c84e15b59ad2dc98ebfbb620ea25d8800361638225f0f26fa394aef30b30116a967ec0c6a1fd541f82a8091afda6a6d6efeb080c1e0ad26577e4b645ddd36cb9658dae88d305d1fdc55f0015d1b457fa072d4873e8fdb1e81db21d53d8c4cd8c3d14ce58e0a65f6e4f36053c3c0c3243d19046bd8adb80790a527dbc5ede9d8ecdf60de807d0e924b52c138cd1ac03a04d8e88d07c6de29ec091c14d2da49d5d8782121da331473e86bf1bfe45605de793e288bb1aa71066aab84a29256bd7b881523d47ecc983a2aa4255097c5898bb5af93edfa26d5015bb0c6cd798907d7713346c22b48aee272d38248ae714ea7ec5b82561ad2311fd201458f45a4dd86e7a0f108886af9c8d7613617ef0163f18e25aa9ecb792929c4b0889000b9a49cbc9fb16aa12d3cfc54184f17e50144038d3a2a3626f31e2d9fbc165df3f9fcd75d9e99f535022ec91b19bf7c24a033acc8944dbe79ccf224ce84f0ab04273fd6974069d53931a3a69ba7714be5da0f234df46855e054e8299b8a573817dda6b09843956df7a0e2e21d4ff9d91853431e11061482a315f001d434871e69df4e1f24275172d544c130a1c6d40b3bd6b1d7a3d4520217577129eb8a792342940a31d20af3a99f0f4e005da9e561e5237598bed6ee3ee3820a170ba23dcdec74b1b3b461944639305c1596d3b64c83582a758a345624f51be201d7688c69fddc0ff0eceebb2de6d06dfd6cbb7047857b1ff42f680eccd1ac212b73a863059add2efe748ac8971207921c1195f099f9ab6eb43dd55a303e7cf52525392301fc899dfbe7408424a67c509243e5eccd4cfeeeb00b78288506331f06eaa5fd76a09b44f56feaf2d7af4d23b05308b70ccd0c1d5ad8ad1d47b6ed200fc31fa90f5a3102d27b3b1245948561a7259fef448d208a4d87472bb58b647024fa6e040e10f43ebfa76dff924cd8eb7b20dd80bd5e8cf3253fc75ba2921ba21a0ec8828f4e0b8b951041f4e6b816ba41c45a187c8a6434614b24a67aad33d81226a98970af6c1c254981f8fd1d3325244b579ba13038c2e15e670049d7add31e5883b5055d27d3764f707a699a7c34dedbdc2dfa2388b865e81f2f476a9d74033cf6829ede66868d1df503a99b0ab65dcc9ff4581d674e3cc0edd62f6144551a124eabc55b48029f4d416c598c41215d2e3517fc86598aee3d3876d356fea301e68d91f485409a6939dd9136e8ec513ec47d57cfb3f3e0c130160725b15d712c4d57fb83fce70a48b1f42a06f7652f13a403c42e096aeffd06aa22ababfa6f16f60d110b6e2b66ea8828bff298724e5b5fcf493b2106050b85f98ed06491ac17b4c24d508d47a33aeff7a156692b0f923db58fae1b5cc154be223e28cf7085008215ea995ef6c42fc4c9fbd66a6bcb70b2b38709054ea35cd475bb49de59e10956147508ba2e4fb0382e90998b369f03ec4b14d8d41369fbac5544b4139cbc2bd1cb184c504f95bd229f7df496b1e8148eab68ff7a47356fcb4370c00f9a452fad6ce685e37ef2fbe60c09063c8269b6f4a4f58501bcdd6516d59ff13bc307194265fc4a499426ae6b3d0dbbd38289a438005d3a1230768fe7f22a6591d73c1897db709e4dddee2ffea8f9cf5f7505c38eee0e056bb84b077f668fa3fe29441212919a9819f2e3e22e177096168d5e245ef96e5c116cf5c79748d55620b9901ef44b6da68ac3effcce28cf482c7117c35f4bef183e4bbbc96512a7fdbfe0381833ea263ebec528a88e53d624614b2be689b4755ffb1d27f661b4f5a41301b90dbebf58ff3eb42dbd35814ea7a002d1e718e722d9adad207374586887a5224a2e1c83282cc07d6cd0aa8d1c5758ba79462ddc323bc3bbb930373ef1c53b92ed2d96d4d238155532d1953410af79f3d08aa233e2fb256778a04dbdd925e7bdbc182a8858a15071189ed105aa6a222a7a4ffccd9aed35c4d7f7ba647273e2393025bcc927bc2fd289d1e434e8d15d038ecef09f44680b726df17fe0aab28e386d24fa7843e6fa06f472c82dfaca32d85fd582afd60ad817bfb11b3cae843c751e02776d66f8bf51a55acfa283bc6442d185512af1be7154b7a2da03abbd4a60c4127af50b915ec98859d5c684edf56b08cfe66418f2a5889f9b3a87aef425f6950fe98c14c6fe622d1d1c2bc47cfea2b721e97c8dbb1d9cb37577cd019545b71f0285121e51f77094251805dbd8d085ecfe8ccea6723dc062bb3fedf7c50593b0205922a540423bc8c3d5e42569bb2e7247ea76f0336bcbccbd1186dc9ce7482d03da45465c43bd0de56532970355c7ef777b177c7fe6a7532f317a18f136fde2224e443ab209e43f772d8074fdfea3b067c5381c65bb75025ee3f9dee77ce4dc03c743c2c27d44ef52d099e299389080c52600286711e9e1c734dcdebff96c9219ba0fc80d6ea186e6c29dc5c3191ebcddb75f9a6fa142abb71b0eb24ea3fb870d74ad181209cf52fb62d6a0b7c45d9f539812c01d85dee2e3de2131e9fc0e9e2df8e785877d96f7453353cd8e66536472753c38f34c924a11f9bcd3e1df5b24225211c57d2be13d95cd10a32f573bd85de9b70bda007117fd932d6670a44a0206cebe7808618526d6bc0230034e66ce9f7e8f7696a54be414b4caa9cd0855400c5b7787ab19599dffb8d491cab2af3858179c4829812ccfa8950148db8b0925224e70668ec89f91317e477701835166eeac74a894791d8eb76b1baaa550f11216c0fd4163f586a4e16ea766eeeda37ace01ec98c628056398c52e154fb4fa782f2af6aceeebd78d7f15b25ad97c9903deecb86333fab6be68bd8c9ff9facfe4214f0826fa40d3f55a50d102bcdcfa14680605bf36c2d142623c49239b8c22c31977245235b6d40d8c19937dd89007a42e79bc006ca2dbd5457f812fa1f32d9f1029f5a1a4d325d1518f606530e4b5d112c7fe4fd991d78467c24a0487c346a3258eb48dbc8290f47244f0dfd8152f2c2b6c12d1825bd4ef6c35da4a958ad6db192114ee30ab3d73821882eb27c746bd064c8d29a5f970c77faa3b11299f4bde099787744f327baf3422124943d9662f27c114be514e8c81cf618c21a009e2f82331c0ce3f8705e19f585abb4fd7adf1b9718854ad54a8bff4281e2dbeea99271874da9862988c3799ddd9031aaf478420767989fb32ad7577fa16523d27518e0a961ab41d2f1a80097e95d8c03919749c46f05967906ae8cc3ea5de2d4f25d8110ada472dc029c62964550f9ce1372c049f582e33fad78b81872c25d5e7c0521a3f7c7a4873e16b9def6fc5cb6b8bd7d918e64498de20f4de19f6d0c344e526175ab4d28c8c1f400c5b7b8cc871003b1d5cdcd40bb673a700a1edf8281ebc4a0e3721c6515c27adbc7da9925914ebab3eb9dae0cf2200cfbde8e446c38d7bb4295bcad28c370cbdd42eaac458f3ebe5cb7fa2ef98f984babcbfdf06f34b8f65091ee205e13701a1c2ea8a843f5c4b6efd809fcfabead21b10bde2d146aee526215a4fe1159a4d459d3e0016eaf546adab9cdf32bc4379606278bade85a725f7158a13d111c9446a19f6b0bce14bf1651b10bc0123a2a09faaf6919b112dddaa0a0ea3a4ecce723e76ff8831e4cd13d7424c4be81605104116740d58fb086a712e326db739292746d1985c1f9ae8899358e00d533a75efb5d7de1ad2742836322d49011ef90a17d991fcea02ac3274e6b2a9d8ac45e7227708a6ff1b1434586a11db76e8211ddca3020b2850fb31e09acc20b25a225d945840db8f189168aaed18b1ffa314189d1a899b0bde3931abf4178309c5e553cd375db69e22ba0131d8234633d63ff52a14b8ee82edebff56b20612aaa1828f1836d47cd48a889358a31c2ed3975a00706170f68e5c4fcf468d90ef43d57ea3f28b737ead30045fc1be2f085a00fca043219f011f86030f7f33ac46e1f9796a788f6b1c600c0cd7f74d21781ea6582c1001799f2d348f46f6a81f723560d7e920c1c8fcbda35d6dd4560fbcc1e3fe95963b4b591935b7506b55e309523f6ef0372aa53f6f6f39a8fced3033a3b0ff7bf32b9347d397c052d1114065d98e7a107ca39c1ebb71d52d79f101b2e9d8bfa85029b731263a28b39c9f0e9400d3dc6dc1c9328a022a74b704f2000bf3cb1c7232b14fed08b9c620894f038b79fa0dcd99081b21527561ef27b71531fbc148df29328c03a958ae4a00e803eac930ffcffab30209fff34b7d3f79102a1b45d43dee8dcff85bda9102cb809b22e2e10f32f6af247f3cd2b5db032c29d5334d06da44474e388dba32fef58f6f41b64ac8c0ec6cfcd4dc3e0f2673f92e5b3ff63c00f9d5296e18b42d1abd074dac590e7c6e16d339e714a8168fe9dc14582423af2f47b9078fea487c501377206684605bcb5724e6dc63c1e7822abb0855c33f180b5b20ebb2b40c27267d315c4c2bc75dd5cf4a2a5bceadea37ed78153883fb91a526e6f2a716b7139fe0b86b2a8900372f0172f3aaf9ee4a2019ff06e9f15dcfbba8dbb9dd9f3a28e9f54bfd43de7e0f8482a383ed5b608df6cbb6107cbdb223a442c91a27b79c4bebfab136990c609a006f94a4b4aa357bf3b4d70581cb1e022e7beb4fec5b5fb12d0c964ae273eb8ab4dbc0ca9e38768d621b831eb161ad789abe47c91993aafa4250b09fbac7fcb9f489d93a0be0cc4980ac561a170026532a309839237bfa1081eae9a778f6b16665de3237f00c8124fa1fd3253636290e6dbe6eeae74358d67aec6b012104749f53bd345b362b75ab3b549d088599bea97a0c7955ee2b01338bda7d05c5579e6822b583147e1d0958e70eee1b1d6559c7b4a21947d81d6d7f82d511133b9ce051588ce4a1a3e381ab5775ad0a93dbe6e14e6f4d040a4aacb7864a1468b61fde0d2aad5d57f75c6f9679afe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed00000000000000000000000000000000d50b7cdf578f4412a219947e71659c0f00000000000000000000000000000000585506e2fc08284102cd3ce553aa726a29012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f17b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "integration_summary": { - "integration_test": "test_trbfv_actor", - "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 1, "cores_available": 14 }, - "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.614281708, "runs": 3, "total_seconds": 1.842845126 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 2.121472944, "runs": 3, "total_seconds": 6.364418832 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 1.95707425, "runs": 1, "total_seconds": 1.95707425 }, - { "name": "GenEsiSss", "avg_seconds": 0.758238652, "runs": 3, "total_seconds": 2.274715957 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 1.242666944, "runs": 3, "total_seconds": 3.728000834 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 18.979502958, "runs": 1, "total_seconds": 18.979502958 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 48.341644417, "runs": 1, "total_seconds": 48.341644417 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.006914333, "runs": 1, "total_seconds": 20.006914333 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 30.277848645, "runs": 6, "total_seconds": 181.667091874 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 78.310650236, "runs": 3, "total_seconds": 234.931950708 }, - { "name": "ZkPkAggregation", "avg_seconds": 49.050973916, "runs": 1, "total_seconds": 49.050973916 }, - { "name": "ZkPkBfv", "avg_seconds": 3.850818819, "runs": 3, "total_seconds": 11.552456458 }, - { "name": "ZkPkGeneration", "avg_seconds": 66.056590278, "runs": 3, "total_seconds": 198.169770834 }, - { "name": "ZkShareComputation", "avg_seconds": 52.534038875, "runs": 6, "total_seconds": 315.204233251 }, - { "name": "ZkShareEncryption", "avg_seconds": 114.608395854, "runs": 36, "total_seconds": 4125.90225075 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 251.230740403, "runs": 3, "total_seconds": 753.69222121 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.093863888, "runs": 3, "total_seconds": 0.281591666 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.264344016, "runs": 5, "total_seconds": 1.321720083 } - ], - "operation_timings_total_seconds": 5975.269377457, - "timings_seconds": [ - { "label": "Starting trbfv actor test", "seconds": 0e-9 }, - { "label": "Setup completed", "seconds": 3.273315 }, - { "label": "Committee Setup Completed", "seconds": 20.279577333 }, - { "label": "Committee Finalization Complete", "seconds": 0.007173125 }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 5158.12970425 }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 5165.210807791 }, - { "label": "Application CT Gen", "seconds": 7.707659625 }, - { "label": "Running FHE Application", "seconds": 0.0710595 }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 835.140242834 }, - { "label": "Entire Test", "seconds": 6031.697821958 } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000821730b5f6c7306ca00000000000000000000000000000000000000000000000027d5a4288c59517c0000000000000000000000000000000000000000000000061a32d74244b4e4cd0000000000000000000000000000000000000000000000000002752ab748caed000000000000000000000000000000000000000000000005a4f0140d4a1c7f7300000000000000000000000000000000000000000000000512ac80c5fa266d0800000000000000000000000000000000000000000000000d400822869689e0ec0000000000000000000000000000000000000000000000000000a3ecc527c1e200000000000000000000000000000000000000000000000564b7881fe1989f5500000000000000000000000000000000000000000000000674ecde0680d8bf4900000000000000000000000000000000000000000000000584cd67f303aef6c30000000000000000000000000000000000000000000000000000623ff53a7e1000000000000000000000000000000000000000000000000b27d89433be674a5400000000000000000000000000000000000000000000000d03b476bc4cac62c900000000000000000000000000000000000000000000000d562dd63dce28dce600000000000000000000000000000000000000000000000000007014317d4d7c2bdca3a8f64dcf8eb8b35863e69e18fd4f152ed73eba57c25990e80b7c74b79013d6c8d4d7310cd110d6e43bb18fc56336d2e4da3740b50e8523d5f9594bcdd812036507369a09143ca35760ebd0173b2c22e17b4dc2980119e4fbf1ed9cf2a9021fe43ba1b07e24480cdba82eac2af02f2e755eb1b42e866b12ef874db8f56729f0b56939196bb391b63f552db3f3c7ec39a1d778f2686df8f01eacb03019c80d8bbf928d3be121e24970b738be38dd033d077d66095a42d4f1494b44eb7df0074e45e1376a363bcf636637b442460155ece719bb40d1fc476322ceb66ab5261818431b676c3ee34900e20139f2ce04bedc6ee159747b931b42f2b22b3d7198079fe5ff134d61e1d32094cc83972db87162171f68aff7e77845fde2854f3b9a0d21b74b20d8c2ec809d9db9e189051c0dd8557ec1661fa1a1b0ce322590aecc23e610a0c95bf8e2f1ec67fad3b50a0d2e04a267e7844e7e78b710b17602724a2b1fd0daf5aaae67ae6e02faeb646dbe547f0c699e2cb1379f50d28f36ba218a1b615a728d5b46c9da5f17803b7b0ddfaa6af8a8d6d945602cfcfdb1a8c2845e10ef52b08e4afe6f9717aa1865a5df615627ece311d2bfccb7831e0631a7271a08ce5712e16458da4091348c134bbbc0f6c546f14c8d9c231ab7975bccb9e02e0c991572aba8f415ac14e2e466eb01533edbb3a35795beea6327e635c6b103740f2b82ceef34e542ab504d3202e68b230920bc61d94d1cf18cde724d06e23a482b31011ca389f49559a044f8bc99e2eceffadc140cae4937d763106987d7fe30299aea525ef4ba58a7d10fddd62430590152bdd9c1fe3f5dd865bb2ce4e22a65086dd1e3b14c653011e7a3c89c4abaa1ed50be316fd87f42661de510e98dc9bf0dddcf46263bc755e1f5bad01ba6f03708b0bb47ea5aad02d6a9052b1fce2f05226a3e2e74566f10fe938081e85af521d530facec93e917a64abe8a781e246752f984676b56b5a9c17fb37ec862c78c596c128c75422712251c9e25f0fa4352a2a7bf93147adfa3937ec9733a4e035e4a297fbb1e9599d1de9a59b617dc808f117e08e894d7ebdc106e7f065fa3a096cecca51bf047ded0439a19e86d1f4a358099e717b59523313b12586636c72a0028ee88c7cbf053fbe465dc0d546b698cf132800fbde649562ed1d1c65b70ad4ba7497d90bbaa8dab5316435f6473614f501bf945798b6e787cba40d1202870cc74db20387287ff9fffb4cdf7a0160ea04251adcb89e4bed874a70342a96386f8ab23147019c7e3dc8a6f2d0f6ee77ea1b1683337f4dbdcb041c320a5df0f41519161ce81427ee42e7e55b381564cef1ff1036c245bd9b7f0292937e19a58c6f917d8bc00ffb2c098590ffa91e35aa88bf0f29c027c75a7685fecf83028b01891bf2582da15f30dbdadf169772056758b112d80f494136784c5e55d2ffd210cab43b12b3b22efff7fcd159f532727da6ec05f1b98a8978a14a941d2489d0948799285fecc08732988a56e66e352362a6be2cc8b3127ad5279b02a55f57f785b7e1db4de806714cb6cfc421b1af185f408d2beec04fcf9b045a9fe4911bab199445b8cabc822f63eda13db7ea709458a02c27135db9d0e4ba34442524e335b54395b3c5d1d19d856c4a813e0fc2303a45b50eb4e75e5e2bb32813c7346b9c9c1c0ef2b8cccfde4c1c8f4370c89efceb6be328a05d97d39aed2e4cc7d7536b34af28247ede4000fcc7b428147375abfdfb5516e986f5197eb977a865b855e9772fff94c52ffdf99286ee66fc4bd6ff9fa6540e0da9efe06b9e2cb8c70f7bed562275ad9485849152717537abaa41b09aef482b701578723527aa01c63295dddd454473177bfd0415c06d3d4e1848d50a58252bf4553e8afc869321e3d7796ba736c8b389b852bfec26444709e9d423192bd117d4a7cb64bb293e7c7033de3dc675267289c8ba6fcc0ca6ce0b9050e79e4e2d12956851fb28b58225eb8c0ebc9a45158c59a5655fdde45136ce3888dd460ddb15888a69073d825555fcd88eb1a3d7f67a4ebd571c54d2d1274518f65805c2382c12086776a4e11771b7d00b480fb1c5a78c300cd815f8039dc45ea68e61c51e130a50b94972ea631e451d8eb55ce9868d50b4e67952699e020fe4a972b44a36115a8998e760ca80bfb4b64576d97e16bf0d1c9b703a106482cf6872973e94520ce2037c03b5c1cf4892fe2c779650c50d7b88b19a1f6f4be7f5c78fb11783571256a19df3f4b96c9fce648218195e60d1c2e4c662d66ccb18a71bd0a9fb8a8b23c3faf51f7aef24669b70e1a4274abc0f66b911c67f15fd970be027f76d1aab14d557ba7d4f268f95afb5068341ba0900e85cc7d322674c057004d6fa17189123eae8a63d39bfc00d3630a696781bc7ecdc4a6b1d7677ca60c64c32d111d3c10f75c8f14135d4faacb046778787802723936036487091e65721e7feaf94a87d10cac1095e8bd1785f16c5e7f0f5fb9fbfb5faa9918ce2a047c7c0debec9fd5f2c41c01fe8b3062e6fb6e6961c09ab4db1da10ab612f4c5a7328a5209dba00950b62695a4af7c7304b3eacd7d23b4de36c705a55605659d07d7bf79a382923d82ab46fbaff077b68f5b592be6cac56d52f95b7f67123c6ef30cce46bb673b92b0ad6bccfde38ae2deb094a64332f2645f4cde17c0a64aa6cf9b79a7ed828fee42cca7f36d7f6b946dfd1e01db6832cb6f4eab7ea793718651913922d4f360d4b21f257a41c05813fdec7e7d01cd94552d86eae1bced28135690ba7c02f94c4102e5902efa5fcb489bba7b608b977362ed8ef889df360bb12d3f6fa737cc23ea410e2c92ffcb685b442e3884384753e48b3af21056bb1eb92dd975c59aa54ed01161b2d1f8b8e48050ebebc4226dad4c4efd3ccba5c481e80a25e903ed9bdd7992355c18cd5fab093b281f89fbcf4c49a728b3339e0a50b7c8f3eaf38c0a45a241cd2d8c6780c99d0506d6db1d8b009aac9b761cb438e732bebc6c473618f58a526589895742bb0bc9bf2157b0ac83056acd061226b5155c952999f617205e6231dd54dfe603fac9886d0279e7a02a79d39fc16d9590496af4854499544f0d61623b4c14179daa63917368e6315d7c8044998be25f1fff60ff99cebeaf702094322d12ecbd646a3e725894e99bfa905064f065a4bb9ecf069150adf4f6f234c7203c810c6f79c786b859baf6db1ce8395ded182bfce2fd1385b1fae68ce21305026844979fc3f6f15e3f01a641e070dac977d4c4e8251de5b25aeba717b43bcd322a34d4d3c3c7f307f3cbea1f6c4ff7736b1fcbca1b856a1d39a1de2a09e2ff306d5df092ac06ba92c3a7a18fa39a0a90e5b2c1d4d8645f0ffca3717f752afc42eb754439548876e6b43c3a5d94e1bf5abf0e7f9b5a6686050cbba01a352ecc21598da0ad02780190cf24cdcbc430d5aedfc4770abe9c6863cfeef593d95d28b1b050913d0e34830cae0602973282d94fec6205ec8756b4d696623c67d33e55e05d1cce77fdac4c7fdcfaee946a7baed22eb73fb1af5cea27644c13e2e249b6a0fce2f7340ef3aa4e2fa39063e6c8ed748a9d29f33a38f4b50e12d60c03433b81a80e41a565ac3c7013a1e851151847f3bb22ebefd9ff335ff703efe6189df8611dd23fff4444e25494a43eddb53fd61199cd4c1c04def81d6f77187f8f5e3da1a1129ba1faa492ccfa688a8fab7566278f412deabe1f69458de9a4991000314172a67e9eec385ebcc15e24e8cd28e117a2e885e418a5eff75fa6e0af53d4782282f17a9691bb7192b5b670ab7a577b331366962dd0a36346f28b8f56ec241ea0b6552eb0d11d855088e95a118fd271c8a3a1922ea0bfaf0afe9ea5c758e20492391a1721c25d590aeee4199f47b8e5c32ffea485cde82f7392674c9de1aeb3c183f9916e9789575f1b0b461ed9905831051ce8b19952d6c331efbf48b7dbf4614a5b15d0e87af9e76b285fd9ecce3107a983c726ae5d3d6ec6e2baeb34a9e390ee6e7510fc9579db80f9d8ac57e1cc886eaa4d2e4afe8430ff03b6c6f3aec5b2ebfab036e1b2efe026beca2aaeca416a017afbbf80ccbb2e72791f9cdc6f6b505b1dae159a08ee04351f5f2f240edbe45973e734d98f6f4cc9e2b7da8da6a110f7e97eed170300d1f8541ad1e1cb3bf6b3863a4cb490cb5c39e6d4faae2f6ea0082ec6630d36696ea41f90d0d64da3c8ff4facd1cec120e8736509315d426770883f14157bd101f004e3e63407103842843b512eeef44a77743bcefe483286303ab560fa84c14a40002cd93f22ece518b999cb3121c7e1df64eca2069230193028e8d3507fd7d7be0e586300d994405ce6dd9895c747b49f51f20d494c916c808ec85a1e118a4ba5f6448c66d7ebbbe036e81080e7fb9d38cd1ac591d5667dd060cdc04db6517473b7814ad9d757fca4904ebcbc818ca1f22ecf674dcc0c8ec1bb2763dba05f118fcf7500e370108e9004dc46a567294751b7ca51e51229b22220d0d21392e57177d6e2671cf3e954eceaca2cf87918287bfc1f6bf533708943011f277db9e4f9d9ce32ae3f3a1a3343d87d3f95b52d768e4848a92aff7dfed1aead60fddbfb11590d84445c4e75305bb2313c20df2f95eab7e64eb085ee372282e3acefad52cb23bf8855d760a5ded264e7cf806f650248c6db9c54b48e80709642a094184762433ca772b01c277a6fbf700c2046fac1e7c45852dc4c127ec0307dde473f72467a910331f49b44d665488a4c88895342608a78ef07cead2c305ad0fb767e03b49b8e665584ecbb8651dcafc1b5c116b33ba927fa8d15b98f5214a6f4c52b2e56646a8d9cefa2258846c3de653e2297a4051385c199d278a5926a6a8f3f0356900b0f226530e88bb4714c63d444b3dd57e11227770c75d1d2f225236f50e50cf07d29626531dcab3db32b7c1a3af3e43febe274500247334660014e70f3347b1f65c35c55630d4db54848774e2c140eb7c7dcba10652028cc818c7a328564594c77c3ca4d6cf9b0ce46a65cd4cf70a6820eabf65f863291f0f0c89ae9929974dae20ebeb9d518dd20b2795765cf0b0addfc027806a744965d41d9fe3385a89d328e2f8a5dd616160cda986b7d7ab9ec9543d99fd0f70ca38771d00e76228c39527af15a012f227bff39d231a2682a9bd9c4b45ca1fc2b260aa250577f8fa880edd4a07dd8115511301135921bbc68b53bd2375c7635fd9dee70dfc0e14e099d417eb4d6035da9ff7a9f647f84df2437aa344deb5737717058d0fff3c23d38e8d2548ee3ca6dfb23b0c4dfd22cc50c86134291cda45e0131e1917765092935098bb1a016aad380ecf5edc52dd0f95e566d538900955bcacc4342d16f512ab3e6aac573cb9a03082605ae8a4850e455b5d1641c03a37b0cf6ff90215793fd21ee0fc330133beaa6de3b5d59c40755ce8d0cd2c434b9a228fb79815ac089a2735a487f497d5758fa303de789f3ae7a09298147bea45a9c69ce19e2d058fefcd8ca169e2ba7760c96e7af51de2a28cde2d2174ad0a910dbb954f6e2fab7246831353872aec29ee4df899bca2eb5001464c97b1cce8c138fcc94ec40c8e76d15d58b5353e6b3f9ffc5b871ea1d2cc1fc1ab411671eb7d4f80c28b051dc71f34aaceee78f29a56e4529ca51c856472af3c41d587c25a3bb5ddd190062840ac3eb0fe54d3b1f28f63b89977405cd88ca9c73df613c7aaadc6965661c70747b5fc78ce643b6811e78b86b7f68736e2b356a934001d2ea264a89cfbe99a12dc049b6f9ec05d04083fd34b5647bd585f1be04af90aec97ea1e1ee9d42b841d7f31c6fd3853b6c12510c173a710adacac5b23fee0d5586900c83d9711955a1d5d41d21d27fee28b4989409d645d05feda97d3b12b1bcf31e156c75bed1d3d299392602de76f62b9103cce850a2b5a875ef961bd2f58321bb605928c80a122147f4095ff4fc0c4772cc9eb0052f677550724a61d0bf0d328fc6116e13e20ba1a7ab9f9f8506aaf4d953b7c13ada2220f7f6f4695fffeb8a712b5cac9f185ad15a710203071669acc41cfcb817ac9aa6d2ed7c763d9de8b8c7ac7374b0896db120a5f2e718e228bd07965ea30f302417d9f9ab3e01991d4090fd2b0920194ab038128867db1f90f4a79b4586f7602b80324cbacb580b34263c724430b9b91632d8d497956dc8231e14a4d8a202676f3b8a272bb1e26122f025c4f8f78d5c72c21b998aebd00ed34be2d76e4a43006fe81588d6b23eb5bf4c3367285c9a9ef2927a7877682d3af2d5e33c05808479f4521b3a17168e6c97bdbddb7770f93d2f01b08b7b99d331e78b71e84cb19252fb9f9f1ac65e844728db5722e55ab95b9f41edcecb4f31deb949896cd3db5cd0dc2ef15e4ba0eadb9bbe2657b321e467ae20c88064c8c15e19067e28939f8ee13304d7283a98fb49625609483ae02f2a30d160db7d5914120348062db5917327da32d027a758811aa7fd1c21b6a1dd8c75e09d22df3f7027626e95fcb651e8b7559471fcb5ffb9162d550aa82338c51cb0f02c653bb8a2b59c2959f293a4973748870484c05d4ff511fa666604692f73cbe282d4e0e243d6d9ba21e950ac205857f6d41ba6073034f315ef27929d5dba83205f192e83a1e5003de8d1c38c6dcfa2593bba4898090ff610b0890515f9bd44d06d0e8c2d93df6d36626472b3ceba0994cc76cafb660d6a692e7bf0559db1ed02fc0cb1865d7d25f89fb247bfa0e1a3fdae33f97abf5ae7d9e35033050445f73244d228055862735caebee5db8a119a9d8d11a7035555ada83e299f3387abf5f0000dea04c2732cab2470e3e04688d9f1dedd763e036634d28ba9f8e2c5240ca271898be20fb585b49b60139770919a34305e52be70f0d50192770fc02517f652e1e7ce1906dc26a8fbde6939f69b2c0645b8cb4a3954ad2b43918305a27046603a85deac0c770c7e523137f35e3000a0bf96b8c329525f65957437b757c262a2bc6a9c8527c1359d8e598afc781fc2e7ed7501a730554c58d2a552329f99d5c2635fd5fa08d5535ca03c6ea08662a9c52fbc5959b1a998374ed3f624bab2b5b2c33c8f5aeb856586abd239d13286104a2e90e79983c92ed579bfd5576668f7214523b4f53f4bee84986ad86798ec100d603f863f155fdfd2fd2cd72f5676a852c49103b1c0c67f55c4af21fafed6d1d9ed79f863c9807eb055b1777945f8d4f1e0db4f8234739e50bb75bc8fde5af7ab0688ff97d67209fce8a9b39e7b4d05d25548e0d72bccaffb3b604d3b33ce22039ed319792b1fa674d396a6c9786dcac294231be2234a234be2524f2f7dae91c55e58074b0503b24530a0c6e6297f19b0e695dacbd609ce9ca7cc5b2511ba837f4eca002f7102967d35389efb30000cb181f2a3daffd5d7f9915bf8dd1de5d4e7659638e9584d13714469a9ba5d37d7313784f05375e9c5e34ef8219292be23da93ad73869d0bc71516e4393f5a782062975871276d39f2f41d36b81ebbc221648befd2dd8b9ae5610fabeaf54f1947f17dbe4490a05d99f3ac74e0d537673afb9d993294d2aed3e7e293ea0d280ceb019e6f0704f9b34dd1a8da3dc616c365e09b09075d2fef5b1ede4a3bba6df095b1390ce5a31053ffdb83cb05b9209d37592156cb464012744e90748e708378d9c04416b2fd032528cc00e516f1fa6bb6bead7b0d0a69d97e0a14e3b40ada5065b101efc0f1f4cd612b723e18127513fa2b82d5f5cdeea70c60581191b3c7cd81f1cf121cc958060e951b4e6c76f2d557bdddd63f9f51676352d04e0174aa85a900b4a92fc1ddfd06e8006f1d78401e5f7390ace2e425bed97aec36ff7e00d2f6525871edfb56c38063860c618feb09c8748f76f88ea04b6ed77e5421025254d1b2ed05884ee40dd3e9536f678676913dc230188ced021bcb986b6b18b475fe0ff25ca62e1660243f9d9b164881c2398ee00b789bc8c4444d3682420cb869165b30e224323381cb5cc73620a7a96119458441256a6229f2bd621cb86ecc29165db17aeae0846231e6b67942cec4406b326de3bf6da88d906298be18f50763420171db37eb6250875d716390e1399175ee4eec8b0bd26efb179532d87783746b5521a5304ec07e9daa765361558c3187a2073c503c3981ea7992d9c60bcf15ed2832f492d65ba407662a090ab3ef91cfcdf450a588b143f4cbf6fa16010ea41310c305c41056c1e5d407b1eeff592b7fd4f7321aa7e2be12587c37faba2dae3b5050e6c1530a67f22db6c8febe5f34295ef550fc8bbc338a40623dc6065c708338604f699048cd976fdb44878c7b1059cd85f4b91b964b8df3bf9c70f3c2befd9322551cd9f2b50fe2d058916efe06f883fde51721cc8523939a3350133f533f1810524dd023854b3ae2592e3c34de948a860ca68eab2e7515f901ba4ef130450802767ddf18f04412c57ea96eb43689d7148daf50f18621a2a2f4aa76ff98011bb12d62e69c1c4d4a8baae33a8117b84857e317f8562da97c5769136574dccc90706388d13056cff5c7444a3beacfb4110d60cb4a41e70cfcc8de69c72a693c3c22cc4374fcd2cdef089cdfe6a0c2a0e8f8acf27d8e36b650e13a8d51308a25a6829c36bb3cf8ad207df79717b3a3fc45a0dd3b5db984b8293dbaf91f785fe0fcc0c2b38ec1232d147cc42ac3808a34322fbb5f8306303cffb36614394275526e41f473251fd02c680ab57b99de93a2ef421ab91e4426b4c99f3c6d03504de269b275d2cf9faa91d300dc7bbdb3dda6da71347133816be7b051d48a33903efbf4407f90419f1332cc03e9fd8c41e24a26c8f42481b2e9fed3327ce1798263434e51fb358e785403ba67331bfb0f3133ead7225bd117acbf86d18a8703e1d3ebaef2281f87c90eee0cdd19c503c9e39020cf0a6f0793d93dfc23d0167592855aa6516320c766eb5089fe5089ff9c2a5f43622cca3fa745cf3e61a9ace7b7e1c1efe1bdee1c9bf8561b342a2cacb2584c6ae87cbc23ca7f21561483dc48e65ed74d70e36d273946c8af8fbfac07b8067c7537725fb15dd74d6fd8e06d9560d04fe9e0bb9cc66a552723894d500bcc67d9dbbbb2f7def521d214cde49257da5ebba58127ac3ddcf681962da25f22c4452c92bb8643d2d51cd72af07b36ec40933aded2a7ca13c7f42fad892ec2a215c0b050a497585d5acbef59321362e9ebc54f7d7299414c10a9a83fc9beb917a3272cbfffa4e7da3fe84a0f10dabec1f7e5a897e0962a0e842037b4604c054cd61aac3a07fc1c81f3a72e51e2ec990d4db00b9eb14f32933b1cbd476385d2d315eb05da63cf9aa7392e86f8789f1eecdc9aae8fc14541b5d0cf84ceb4c04ef83b692a470b986b75ae8fbd52ed926f7c50a3f0a5c0025385e05933854303b5fdd3eb968d912173130bf0b081ba8ac3f93cdefc9a8143fa61d6377db816b1367c023307d2fab251d5ca6e846a272a4577a7fc82d0901acf57ea102113af63c159c1f5d0d91078ffe6f22541bdec846436cb3729c8126556cf495ef535047d5bdf6cdb5aff1c08ace3e8ba81eecaaee4bf99b80bf98026d93bd1b87cf7eb21a4085647627e2b0b2504884d459721c7b979153b14d161ecf00bf75bf12b4c944c0096897bc8c53526c746135500931ec160fb77114a317c0ab237646eff1345dd26ad855f8d7f7228ad671ffa4793f582b208bcd2dbb018fafa828663cf93e8ebac379ed326fcd502f1b27bffe1912716dec34b4189d2949271b39f8384eaaa45e64fcc800b5f805dcfe8e9986c26d7541bf962d1166044483913e751c75bae08db36edb57973712fba0c80d6813f1e051c2af60ef6e117a84999eb5ede01635927eed50f201635f19e297f45c406e75470fbfe8a40f0db953affc8f61b592b5b0bbf515eede750bc8a0593edcaf5959a5445a1ae0c1130526a514a02445e3a6978f1849f50a7c23db9340d42577892f4d385814e49b151466ff20534e74439acf791d130b0f765f9c9b2ab66fda9098c7771834d71e0ddad7d678af5183635414a7ee104cf52afc1dd2ce8fdff5ba21534f4d73667501ddc7df289a79ef1a336f930cdbe947883fab991b26adf61eb7837d582b8e9d15ec335ab0809126826fb09abb2da87e9aedca01dcf1a662b74cc555fab8b65c013fbe0dff90977f2a74360eb7fb0e75c59408389757e17ad071d76c112ffbc4014b0ca129f864a2489339ed260cf0796380943a2618f3b13e66649fb04104ff15197fafa3b047eff947a782bea063ee95bc5b8cddbc7db60661db0e6a47be4619fe21e5918752a3f48836a6630ac27392a10fe2b6a6b292903709ee2f82c75e0d667d9b51d5ce9ff23fd6c7f2c5a87f087748824f9bb170c33a51a704be5f042a94345fd5fa26e80dcc485aa26fa6c6061d6cafe422caf1eac52f8405a6c0aa2bff08666e2efbca4f1baed7f5532cb579708baeb3555ae6b984fab00610901e1ba452a848424b71e55829122932331f5306b62a5beb24c70237eb9c0e0207591d783524c1a28c1fe61f050b6bc6c300011f151959e387c0125b787cdbd2c7241f837fb79f84889b30cc8e5e84cb49a895585e78ca301052c5df2a4fc873db770367667202002946d7dfbf953f113a006ad4837ea696baa9272c58c9af1d6d8e1f0f21da94147cbaf178fb414d0a77e22c33c3fda3d0be7ebcfb9d95daed750008c9a1d8ddc601df5867b8277024e8c2f447a739164cc63ea44c03ad55282f181cf08f3933494d3938e6937267d1f54289a4e1f19d16abd449ee8372fce0834b212bfd7c33276b09f5ad9221383f3bea85ff25427d5deb17e7a510d4104e32aa160ae05861837210115dca42c986c81002ca19b8d3645d1b473134a160d34ee3261287fd51ed3dd9518c3ca8a42d99bc095a056a3a51411fe822eed25ac7b9392e319e63cfbdfa8c25b357407e9f6e2e249f9a75b5fc17b8940b10c7a8bdc0c80ce7c78c5a7da26bef638318da682b033778c232ce708d33e025782dd268861c06d6ea5cd80d15066128b7b0b5d5b6f51f240ea176d0580ef7b02eee47a5cce712042e07964c9c052cedc831ad39cb143a888041c8aeca71ddda00e103d609160b7ed571aa0f7dc432de1024a9572b2f46029fe8563ff2fe6046cfbc4589099a139ce636ae588318153e65aeb57232762e7be53e82ec3c6f46ee4f923625378f183f5c778c1d9420a1523b518c7b970ba291de12f4ee0d3abc0daa36f7faf05901731bb21c7b0c1e3fa46a76e005632f8eba62e811233000cd2539f44b06619c0391e67d0b33b06bdfc98775458a4293c9515b87e5c25f6cb6d29918837096890ed9428f0cbbdcb8eb0edc385318bb3e61aebb5774cb91852ccceceae70c38cc2056a75e1d63ae67c774a818ad111bf39acba607cbe4864fe74d45aaee92912e237f27cb4f8d40f7029eca2bae4caef946b9549348e37161a6e56168328e03490e955c18c7bf6cb2ed0f8a4c94bc635ba90724e62b29de6d7fcb2249d3951b32012abdacd304147bf0b87a684c56a84e4654a1fdb1c2109d4210f7e9031f3ff9280148dad4fe9dac49cc6815a9d2910e9676cb332990e6564537f5a787e126bb154cfff073cfacd39e74a57c4d0d382155eea6093b96ee0e0bc1d204f58742131e755596903ab9eeb3e947677bf81b62c73f504acb1adc7e4dee30c034ab75790ebae3861e8c094437ac0d5cfefa3b3b0dd75c18b443e46060bb69f122660a7f0d8147ccd093f2aa6b1ad705f3791229891faa6f27122529f42e149eae990fde118d8dd0656f0fa9f5267beb6ccc40fdbbcbf34de723c173979387831ba95cc811e1c79446cb200b31e31f3200a287f582be616f920f3b24590529f1abea7c180a6ce5ab8515f5498960440ddf5000079fa233245d1df8fd688ade85739eda4906703d70e7215b65746facfcfe1aa1ab8c7c817b137661d535ba08aa63aaf44f2b87690dc15322b2c5cb26d729a557bf30748c7dc60c36a9610c9e758330ee4e1efec3393675cfce7c64da0c1fda8ecd235b8c76e3681cff99daf8473c686d1f0bf6aabadc7acc20b88f11869b498b3d839f679d443073ce6ef84512ebed8821286504bf8db57ddaa7f07b0ef748604de3498c5a8be6c3b5b8121515bb8306782ca30bf541db60bd1eaa09ed005970e3c80a2c7c143f6504d10b208ca352b8880e915045e50f5ae5d45f7401b0f70076e20ca6635e89bd950b832ff0aaf0236e206fc268a7f893c058c73030c70f4c7deb51ed388a3bee3aa16534d5319836be14f0addc9788e212172376c5f8cfae54ee2cd8792ae628061ce47cc1a22869761305fc7a5369bbe86b6aacfeb72427f397e40e8b901f5b3e292d7d29160a11bb05979b806f46ca206ada7f1ac05c54c4f49cfadcc39cba3e49e3ccc263ff77c319ceb2c976e8f0ed37f6a73c371cfd76d1b82af0a1e58a6f8e6c0068076db9c229f59e929d9547d2de2db72aa9b89fd37dc41561ab1809614d396d06eb5689f00b058cb17c2b3cf46fc636066b4632a5023dc55731dcc76db3dc1a0a0ec84c900a469a7d17720805a3c2bc7ff493a2df926d49a394a7a0c45c7620f051b2ef14160d19492657bb67d1bef596d29b79d29a4babaa5428795879ef66d3aa7dfd7807592edaf9d568afc173e452e112179d987725eeb41f1182a9f2bf32db42b63b1898abae8d596b33efe7b6607af0079dac5d356bd7cf13d10d0a4e0768f2114c25ce2889ce6c34db05ff06fbfa4b955b018546e27cd0bbfb2bc0cffa0792cb5823bab8a977dfd68e70f068ad03ee38aae0e7d49a8957c3878d643e6d0f65fcec272c725699c16e846c8638f5cec0fa2d981165b3bcaa7602938f4a2149ed71e10cc23a9c5e7d0a3f5461709b23cca9f4eef68bfd06a6fd5e505e756ab4c40a8e2859e0f20337582b5f4556a47c503934f1b8ce4ba7b27a31560d3809029ea2d2067381928499d6943b040cef2cfc40f7025ab51d66d46db1eaf2dba55ffb9d472f04933ae5c2edea96a0468675bdd6c86d3446009068ea089303a1b9d053a8841d98c5b5a491cc1f8e8e260a3655fd3a6b3dbc20beb8e874272a67260b49afb0213e712579743fb62f8ac169ab6500175cc164c2a5caccaa675159759f708016141c4e101152c0e8e55842f9bad78fa3d8c4ed129f91d6450d6c06e85338347f0f6a91a32ba22a390cff1ceb374f5e2b6745e8905f0866029a4c2f9ea7299cf31ea5b0b47ee4d6329b1ba0f1ed46fb0be938a45a2db24560edd0dafb2f1d5526073075b6b2bb62555d44907340a599754fb868566a768b458d6f6b038866a2512c37b7d743fe40490af6e2267fa03f03e2a8f9b6d95650221eaf0b4c87ccf6d505125e1dcd88c3be809632f750178fb8b6bae40b12c686a256feeb481b20ec7a2024b45fd5c00f5608c9cca96508b2425fa584455c1c79dea9c947c13e54f2bd0b77f70ae6e1aadb54cd3af5dcbd773c96a36c60a6e94d41f051284397b36e4102431a5c1227d7a2e5eb432dcc11f0fd5884c17eecfe66a1ce2a22b0419b751d117401dabda490ef21888c13c136e09941774ff0b070e9b24e762ea7ff7684822e8d26105cc2ca0b833708f06574954f6b582e8053824a60dbf179e50db6f55c069244550b03c11e5cd22653891fe5064198a330115698a1d852f57c32eb855423c9db7c5aa271f155577ae09f6e5a7e32c2aff1f5150feba43b795cc2584c07001cb655353eec53d999060ef4380640747f65e57196198444330db72f8081f5202a4f8e4f598610629f96fdfd5734ff55a7f4031c8538f9fe80d1dfb973e8d61beb98f1f854d9783b319368ae6f9a015484bbad829d30cca00981f840ec563a2e24c11cfd9977d1f787fb0a395ba4b1acfdbf6f2bafee57074e5328affeb76204ba93ee5b82ed1dded622a843db90a4d19c0b4ef46867161b3f6525493b3a3e285408073fd97e2f53fdb86c06e9845e8d410462857a2b9243b348ad17b662cf06686483db4b559d400f33726b232420a522d3a4a63376d60b56f5ee7cac6688203272656b35c2a109ed301e8a298fb397d7b66d22b4867b9806269b28a5c16d2c366e20f6c78ecef995bf41e82d6fc7cc578e293bd917b60d9bd6d1dedd21bd12b872b9d3e443055b534b753d81d39377c29f810995cf6238b69779a358fb6208eadcd48848b22b262ad65f2682172834b865f293d2fc5cc95538f242b21cd12261023dcc253992cbc1c548d996c675b14dac9b530fcc0bf78fb90bad6027e52502201fd2f781fa9dcf4da5c91b6706f2eda965b330521737df12d13d350c2506a779924f2bfd8c634cd73c84811da8534e0b14ee42996d6ed01f879d4d858605adbb356eec9115a7e0a689e0de4c27be333631fcfa9fec8d02744e206dde6110f97142186a44a6f4f220b1295b4e1b0385b0826984a145ac28bcf89a9c3627084ef803b0cae77c7eb29fe2d6ff15e3762928f2f1100f4a8b59adeb81a445e70c54d7b8453eb39ec2e05e612d1c0884eab9b6d8792606e5ae8627d0f4d44beb2d4306f31b6cdc617da0f5374ae79900cd9b9f693debb14d59889d5b802fb8471c0a414faa8abb86d379cf24a883de065656be4205dd39718b1dab3ef93ba2e5107d2c79ae5b564bdaf2971a6ea04febbcf5e2c7b3e3743d8b37631ed2ebfc2a116cc9f8b27037cb4d7bd4078692ed74caa2a1ae3458d1eaef27cf48870bd95a", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c91109676183000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008f26088c208eab68279f6f45f234d271000000000000000000000000000000008ec0f15e98d9058c0e708be73acd2b9d2197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f2a318f2fc85748ea90c49625631654288c77791d8dd0792e201c748ca9863ac417b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e2bad40c7a2fc975f2e4811607e4d5fb72361dd1af1bb2364be799270aab5c6f623db21355d312ad82ff6334e39518d7aec226005c56d124daf506a44749cf370" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000006443e4dd7cafbc6fc000000000000000000000000000000000000000000000009a5b11556957a2fac00000000000000000000000000000000000000000000000481d8c87ec8d9e44400000000000000000000000000000000000000000000000000027e3c69ce5a8800000000000000000000000000000000000000000000000c89919183396d2ee500000000000000000000000000000000000000000000000156b5f6351b4e1677000000000000000000000000000000000000000000000003fa278d2c3f3cc83b000000000000000000000000000000000000000000000000000257547a6577fe00000000000000000000000000000000000000000000000a7acecc082a3669e1000000000000000000000000000000000000000000000004e4ee60a9610ccb0100000000000000000000000000000000000000000000000139a7b0dc64569eb80000000000000000000000000000000000000000000000000002797aa349525b000000000000000000000000000000000000000000000000e4efb10408e5e22300000000000000000000000000000000000000000000000565dff2497b05203c00000000000000000000000000000000000000000000000c3ee606bf31b8fa98000000000000000000000000000000000000000000000000000144da9a140bb701dfc1ca1fdf6f4d4992fcf7bebfab3317fc2caf844b02fb8ac4436d5adea45f18531115019e88ab0ae6f13233c1014519feb8d5364b2b488e9d836182eba9d800f3b9f16b517a82e074902c3c99bf34e7be3eafddb913ebb2c8d05595a159bb2a543541d15947ed4f1c1620e84108ea3ec32503ad7d073162c30b94474e387c0cdcb845177847e28da58f2486d85540c5484ae86f2294cb46b4cb84d0aa3609083d483a8b920f898636ed7798e980729ee4ba163dbecdce8b02670604ec9b920e6c4aa8dca3ce558f7cec8fb7f4bfa1b0d4371b18bc835220613a1409fa080f120c539f947fdfeabe4f2a16449a964098c614d6fc20e3e80d0403f0241f5d8a13147d749da88cd2a0d8266d4380e5070e0fcf87e2e312a3cdb01409afb8ce082549d88678d5f866e68a4d0281b41a6b539a157e18a18aa47a9d801ba82c15b4285ed10e2b52a839fd1fb5a55f8de22855f4af96518bcaccc2dbce494a296ebe26360d30cd1d486f49978b029766d49825f3c54d91bd255e22e279a69caf0742070495855195395e1742793b60d1378c09dc9c7053216512cbd2452209ec4be4071c5a521b696e975b1060278c4f77f59bb6ef505a7223d5245c2faccf2d7a7b17bc6a08396d8036223bd5f0ae7ed2f2891a3c4e8846b79bd36ade7b09fd6627114aa182ae1731eb6b9cf19b8b841c3c2b6d8056888915d6f3833646d375991e22759e6ca02fd6241ed37565eeabe5d1c1ed7734680293580c5485cae983d60911a42e17e7646d7d5bf1b3633d1bf857ea6d0d6dd6ac3947070d93be4e391a921af9366211aa3384aeeca7a871208ff44293b9bc62fc7a31c346b3bc0a41d8ec2faba055c65299d4680c399879ad3b09b91ca75c154614e7c2365c05d9e1e1bf0312970f67ebf20490f3b18aa7c84bfbf44044a62091158a689ffb7eff443a0d006f17dce991bb9008594904f9599b39252e57e6a7ab50656c9e5a8df72b58d51b7c9f4435e6f52a478dad1fa0f66a4117c91477773fef118f1a5b2e2246a83126b004cc496b97eaeb0939d7fe1fe9df4c45268c13f7acd87f8d7a7251397537066d564a275044ff3e9013c7416550403fd3b89f31931a1c9bc4c78890d6039319c12ba5bf473841715e3058c08b58f6c885980ccb7196c78fbab8bfc448f50b23e59de65950bb9e7523d7c99364b96ea7e2b23207d02a56f9a6ba47ffe607b0110784790f007d11c1a16dfc27e06e1ca443bf4740ab87fe60bd2e4719005c8706dac42041fbc232faedc6e10c6a6f75b3fbc206b8f7c1648c1104fa3deecec91ef7ed44600c3779a8af114f9e25af821788f45bd741c89b36e53c6e5e6e983e23f372c640298eb2ca3865a651b2add2714020d6de218a78a66e53d5eb9c037921c9f13da3795f03f8a780a09119c28b795e6e6b97d67250bc77c40a81ca7253285c8d29c99aecf5b4aec5316f45c7934a7c13e79ba5f526153174eac6f0fa7f1d5423429dd2204d5a0749d4f1d72ded1463077de54e2914170e854d559814782791a449f5831160a02643aab224d9ccadbd7c12366cbe5895233711003e08701b69ae17495eb9c96cb4741fa95d3a2342926bdd5a796de050b055178f9e91ba2582ad1dda122e9168349f910fba42f2389be4035b6a4a0776ec380eab4a7e1818da3933777a20728cd32e8aeb3f6dc1c954b5e7ee588996ffb14ff769561b1427200b52b53eb0c2aeeed7d7ddea931eb9dcd23c5652b768cf3fbefce53500b8030754c5c1ca1108f3ed46afb942bfc01b032b26a8ba44e62c0365ca4033b1b219a80f8a2ca3ff9ad952bbfb2493969d7eaa90cc4aadbcbf36dc76a0c848bbed2978369892c56259b660b0a07ee28fba8b592f129d029bb31b80535d8003742403319cbb781f5e4270e2aac914ced8a3fbbaaa3d9f1e834bad9b9f07b44814681c3e7cf0859f312e121eee65169a441b1dadc576870842c917a36a744b4f4da91c33208fd45e9c462ef67ea2f432b555a46ded01dc8ac89fafc61142efa714650817710edcc68e99b475b7ab403a6de405f94656a27d39fe369e561b26a5457c2921ad7d3ef554ad5330d553e322f95d5c40c50efce4a009493e71875eb2182d0f82765a780330127718248a51508491c0d2bd80766fc4007dc818073c337f882d78d25f805d23f12d1db526fca5ffb5be0e54a6ec25e46bbda65b6bf5aec648184761fa18ff033b31c97d42fd3a43ff3f7ecaea4d304d336e0e917a0d2e4425050be6d7fb830cf4562eb87b213a5d6a0dfe7b3d63f8e11e3b1efa9411183811071ad9e3d533211eb93739574879ef787af1314cbf7fb4e874c3ce87ebf8c3f60f93f0b8ef56a6f27811b193c26971d3e773e76e68c5daf56140ba20f67be6242773bd78452b12738a0ec3b6204e93c9377acc0358971d74ac9bfeaf9245d9421a7d20b5a4a2ecd40e4d23d71ca5a7e131b932acc89bfbd6866b38719ce78d212cf028cef1628129d642240f4b2730357494ac27446f5e56c0978e0c2fc239ee112666f0bee00ed47f0164d22a5215d5db82f3ab6cf249bb50703d3dc27d71423028a4f72123edc079901ef7ccb56bef61f6591b0477b23cb07795773f20e2bb12e5e0ef459fea65851cfcc4b387fc42cd5e71bc089a7f4381c913855a4f2f1f0106cae843799f89fa4b279a364f633a37e4b2640c69857e61279dd803ec5c6d039606c9c10584fac7c1d5b10de7b25738d89fc5569329afd79e885fdb953cad2cc05964e713e4c71e4c07ff1cd10c406ff55cbd851532a4839738d7227ed257241f748badd25148e8ca89366948ebeaee84d613987f693e0d213aaa594db3021fe702e566a8ad2d75ae4f812859398669a51eabc475fa34a0d77bb9611932a904ba84084825294979e0ede81171919e8bbab7354cd01123a8d61d9548b311ce05d765d5f5322a49e084b9796079b3196f5a5ef6761db7007dac93649c4a387d02a34ad68acf136903543014ec4b3d4a3fe900211ea22ea18eae5100b319d329034f88cb7f95f41dded0a0ad75b68c71278a61b51c19c035158fbb8259d36bd505735e825c6b8f845e8845155cb93ba122d3e98c3db8da44666609cf7192ff0d15d728de03a78427d9b166b5ad8555e4b65afb3244bd53993eb496c5cd50833d22341df3b6674d72bb8271c4b82e7f03740f15b18a7ce3fc8943f81f826fc8fd227bf5ff41b0989a237271e140ec6ba99a0c2c913033c1dfa00f9613114733a42111810a91eebbb5bea13246518438196d97dabf006219b2203cbaf73568d7f8077e01b07756ea1d188ab0a2d7d2b901634618048e281da916256acd484950b7175c097adab59b620eb4718fc51493f0566c9e30124ed27baae31fcb691fce092acd8a3e8ea251794afe8d35d55ad7037ee89476c111e04610f219a1aee124c22ac4339cb28af586a14d986b328dcbc25636df3bd150a77cea7298745cb7fd6b2ef63e7413dcaf3505f98d6569f1e4c8f3d5082903c2a15cacd73e75fd8df54e2307f9e71217ebbcf22bc61abcb8690a22409214cd7847da52e761f7ced049a31c763c92153a0b2904739905c9cb26452f4f573ea8f9a7740b72e854a50ec596115078a8b11c57010a27a043652ff97f51ca448f30614810e3765b8a4149a63e291d1683a4b9a0b964f6ffece7dfb0cdf1c4ce4f0fc04be99672261f50284503148cb9351ff4662a49ec4803560cfbc9165ad379fc90ce53beb5ab320807a8d91d0a10934a2b9e4363934b89c786d314b1a242e9128fccfa473dd4fa8f8496710aa112f31617661961f5e97fd2c3fea061bbfb54946e7687ec43dec8861421bc140c7c1bdb0a3d9b5909cab572d3434a2431c3236a57baefb073fd1bc6d6d1912532702a84570676a2eb0d0047ac4b2bc9fbe5c927e3dceb3c5c249d3cadcbe929008bdca758870e554bc839114d0ba80cca1fca48d28166f949e52c54e6928b1b9605ffb5f5f6b3db23931f7e59b994d47f7c474a3564aeb179664ee01f91770839111d61a992bd1ce5fe02631dddf3d4ad212e79a3aa20c635f97ea8d6db1a1bcf6ec685335f5a88413279438d62e691fc5848efb92ad7dbf44ea4e6f23a8d20414139bc656a727e0ebe69c8a9dc3802d314028e5a8643622b705ae5ae82e01e2578c9d36a252ed8138a7aacac14849121aaba10a66d6ddde63431e9bae2151950ccdf9487c6874ba926706e38df897c4ca3183893c211c3846182cd2588da2d398a58df3e979a559c498fe06a4a7a0163ee286354b8da07da8b9eef9945c618800d7ddab549c895cc7e2ff3000f1781554884bfeecb44e4124d603ebc19b829730a60168980a8fa287fc8f86c0a8a90ab8f38e71f804bba01d8365d1255930ccb79be29beba86407961ab765e979b3dafc32bb1f393d16702fbc3566aedda089832173ca5a7e5069fad1e0660959d49e0bfefd74d2314b393f2fcccaf682a22bea10fe8179d3ba752e8044853c47f4683b4310914f61e58b7a1df42504a74113e6facddec9a9ae5b220a1d6cf4fc0415e9f725e11104af98a7018d5dbf4b5132e8a07a7b4803847e32293d13af6a33d297cf15e40db4bade0a4774ac86b0b12fbacf70560a28f3f7b4db8fa73364a46bb2eb57964005bd8e58c2e61eefb861e701eadc30caea24196fdfaff46b16dc10ecaea90bfdcda36bd80ec0f006d9d2e1bcc89a79ee552a9d1d32fa60bc21ea126a76871ff91f479eebbf887884f360274da76723b26de1d7a5e8ce9ee53c7bfe8b5f29ec186a407e75ca0e3d661862b867f5fe4b79871eed93603d868ecbefd6a9b1188007d91ab5aa9944fde3f6b18da6194ed89cde701078d634b6c54192dd528fa637977e5c3675f4ada2b502e13be091100049adcf52752f514bf95c3fe99e4e20abd509446b5fc66a8857e1d2001066787b655dfe8931150d5648b382f60bbe72d6e66c73e3e6529d54dcf4811cdcc0314c615042bc66bf5173485c9881e9544493687da1731b6293a9679fb04b9622e9b578c81c7608e01f3053cef3f632d4ef5a58f172e8bf12259f6d5f21e25a74e2fa134a379f6308d57c51335552484fb0e88867c9d243bc181a090cb031f58e0fa9f655929adfa77a05baa4894c7de6a6d37290d3eb16283ff637dbd2fdb3500df9f2418aa9a9f250fde66f091fb6ef5acdb61a27902e22fa6d5fdf0057d28171834d980f73ca8ccbc0b303a5062a068e03e7ecfea78e72f78bf920d00be36994a8ee4f42a36ed3a223fba9826a967f946e75760a85a595015b8af2e24ad7c59e99b87f60a5fc7c76e351614576c43732e32d314ddbb85daa881eeed1b150a822d9945663292ce60728bbac00dc5ba3c649e814005a52df5c0fc59b81011cfe6bed95a7ff1808bf7c6e1984dcfba5975ca1768a00381f2974a32d1151e75c2894ff4581c65de16e354ddf938c056c93a50c61579815a9f0282488c76145951a635f929a71f8e662224c9364e12748d715cbc7bc93c5606f07687844222416ad582df9b9091bb790236a28d090291602b43b80c146036ae1d2d3c911123f37a291933fab7e30deea52324ab9ab3adb486903c9960992b5ad8cf6d89670e608c9b4061890a793116ccb7fbce38857e020824d1db42f14fc651164d22a60aaa8f2ad0f20b63778277b1f641d7fc5d12dae47ddfcb2d55e6b870d8b2814c20ffc73af5fadee89dc3b3cd116e881d5c768520e9fca681290ab5c58ead371022b65ed2116f353e440fdbe5d2fdf3f025e2a6d2e9d3e94c9293ceca8086b0de285a7305750eb30cacef7b5469a0797e42080970c0c3e70f990892b6ebff1044023dd8d11b77a419b3aa19d4bdbfabaac8d553dc042fe6c4555b6a14fd64ec980146ddd60ce32d76980b01b71eedf0bdee8b18f198cc0255d73108f17b509bc13025bafa936bc61579f8c1a9930017d1f0b042c5bb0566743b6c731e05e3bd7d1cef12b035210c3ecf81d2caaa99da32fea59317e383b970e419f861ae2573fd2b3c6b4f0c1ca9dcd2203e4b1be938e7838f970bbbfe78707468e381083605301c190b6c5f06802b1dc8b2280acaab86ac5046231ef1060033cc35ee76fe2baf2d661ab0c19ad8987ab3cbb89c97f3c6368ff099e09d1febe8e7f6ecc1479f972b5c27cc320c875b5f129f3b6ff24b4c8a2eb82af590659ba225f3e190fa4e6b1008897acc6e797f4357055c26eeec55360f23d1da9543b6ca7bb34519652626273e877efaf30df25e18578770476876265c499564d49f34e9f6a51e30b1dabe0c32c7acc142ddde363e9e60696c0fab4def3165b7cf5799816352fd5147cbbd053810b943836eae47e715380acaafecdaa5c56f78ad25cd6beb48b30b71e97224546462e575474e305a384c11241bfb4922692f7ada05ae8ffe3eb5a61f4a432346ea3071cda58fee452fbc4db70cd573ef7d17106659050ca1418bbd2681da0af56fd4cc9e2c6418372609222d7387c272a336e2b34a001899e647750744331b117ae4529b0938e4e4712dc5686b6caa250480220d3e94a35b9632e3a73fcd126572f8c3d4ca698b890cd2cec074e5e7a8a7f6f624f385cd36b9d21144013e1d4d49e7dadd46d3bb09ef8d4358a93c905259d943a18b6a020e9701b77274ef12ca1ec8b6d0d112d3b1dd49f29c7bc13ee5e7ce00c1e5ecce92a8c5b0b5b464094dae99c3561ef02693568d05c04104963444dbfbe0e6cd804ab19753689d2f1254bc80d6a1dde41cae49945131b625cbd58bdcf37f4cf699f10aaa7267e1d62acaedd0bb43e6003a32c7112ec16756df55e6b945e730898cfcbaddc25621cb0f3ea6045ecd0c18e3c46ceeee924bcd8b5931869ec38afe8b79b6dcec6e649f26fe612ab89de39ad9fb9d355f67b6608ea70697d9f7125cfa4831a71b894a0a0bf5eaba34f2e38a421e11ed8e30af9a02bac691e911dfd2c5cc8bab6fdcb23904e79827c342bca3644d0ff0be3bb71ebeddb24e04066ab1b9fbb82705a3266a1b742992abbea7a4b968075d5348ff4e1068b01de9997679713f13c50b558d69224e93ff28adec2874a0f0131ed5937a9a84f1a1d27815ef6d4412936fb2d9ab2c9f461e5eb9aaf0b937abf2c25c62926c3052eebbc13a311af28dc949296b271595a279367d738f840aab4617ae5d46cf23b6a6f6b932ffc79d71daf27bccb2128f76718008bc6be9dcdb3c0b25b5172b1da3752ebfd6f9ea2c8c609d906b690b01c120df01729bd22d73b94dd8afa3b34ecbe74d60ea6d449c60645f1528622ff8a547dac1025fa5fce863dddc98f0b30fad91b413d57072fafb52119e58ca1516eee808b2a74efc1cb43944b1974e17032d21093d286720396ff7399fb63c1f79cd6a75644f67501af7e6d876bc562174d7fe4eb0434c4cfee35ac6be75ac1820791537c5df12a815ba14d92b0f4fd6d78169fa6451cfff0eac6ffe080c5c2bd37fc059beb2f3793a7cff7025761e181b81ddd54b11acb6032db51a7a8ed713315f3711ea9f0a97830ae8b36494b8543ac9b862745c4215db849ec6a8fc6a1c5ee285c997e54b3c9fa212c4eef18b07de6901258f3ccd0dcf9ae10e0cec6028fc22b8bdd77d9f5f5753eb121e13d18332b7ab5e4e1b8da6ed82e9149a8261093fe0c9ae8dea8f167327c5614e10282a690196332af402f400d795d62b79112c1b39336e2d239a9a003fd684b32c1eabe3d12736033faf849ae7b6d13f44770823917fd825b94292e2cc911cc0bd9f55e29597fc6463429b0e3113ce09bf1223150eff80c462c613876c1afb7506910c9d57bcc4294dbf2154e84ded32aee02db67e7e6d6ef0b310397c49c631a159f637d7b54fbba46993bde91e06229569289dd10aabdca78e37e5e65111201ba9db43a5d736d09002d9bb979ba354884f0ddc0e655817c6eca1bf4e8399e6059ce13bae13442905698486b29df39028c226fa402d682da8022ed12079080cfe05a60c2764aa1d7b8ce819ef89b73474a62a32750b401f56656c401f6fbefb83b3fa391cbd494326e63cc172aac33ea37c2ab915cfa8935f6c1b643657122c82828d974eb995919f27f9045f1bdaed542120f563c340c7534d63bd89254d5aeb0962d72358fddf008461ffb28c7ca3331224a274f4a23db46311983cebb2a0c25c643781c581dd1853a98484e7daa303272696b4c3fc3250899de7b73273f0cb8271959eeba9f47d6f27845142bb5a30241b037f5bf9b9a97dc46a8293e61834ce50f783911f3be4c332cebb2a1254d2d02770490657b1d03830ed3ee8deb26801236387b361c6422b4119a60b45c17936281de0179aec58b237faadaec4581354ac0fe01e42e4aa3e7d9660abff18d87106ec366b781a8e7b6815aceeebca6d1742fa5b320e1bbccd556fcab55f5df0fa25613aa330b14f831113d7b0e58ce43bf10ffbb5922d8037fd6e8895bec323a41dfc55b695ae69ae883e77735dc097ad451c8943fcdb85658d42a356aa45a10d079f8b8125a7b007f583ba170dbace2b0baeb316a85c9992f4cbe7463bf97e420595f7e9889b31842f7bcb87ee9e1659e67146d3eb1738237d580ec4057b010007e11de07d72ef853d011846238a94be1d45b6af5221dbbaff5d7f2b345aac312ee1f0ba76c97dc811aaf550748da69a8e703e30d162f6c4d5e3bfb584709c610de2f1e1324166ce349aae1e7925db177f435d4dea9943c13a1a14583bc4806811ec85e76d455b6ecfe19375719b71801e53989ffd31d68393958d5db8d28f5e23b717a3076b3f25ae79b373b4c2f8b74ea54e68d5b611508ca732b86b8e682d0d2fcf22fb341c462a809a437c34275a2b1e99cd2f306ca0e5c471513ca2636b189a2f7b2fd610eef2f1f564c494ccf64b450345ad58e46520438d478c922304080088b7ac282721cda9cfa89dfda2df6b663c9e071514b42a6e3e3d3f4b8a382289a0b58bdcc4d9e19fa811d6cf64b4a703bd072cd660021d4d515e59e6df6b3046f12a2efae22d170874e2650dcef1a24d6974f77cecf09d6fbfd8b34feeb40652708e4d2e7170b5938e06b95d7239537f7ff192bded21492eb0b817d58b4d165b06c7bf692e0321aa3778a74dcadc77994701581806ecddaadf49f7104e7f0bc5921a00718a37a6af9d199e6e7d94a2bf032e92c0eb564adf2a7fc68f24701231b482e813783b1f35ada11c23433530cc7bb6fb050f5e9ecd90b18268a629098693da7c2767302986f142e06ccea3467126f980182235dd26f84f3b4168402ab0ffa139de9f700d94fded6ef3da03e6137e64131ab66dbefb912545cfd9ec2140d43b2b04494a1cb5de412225c593576471f8421345c6461404437cd50bdc05e52284204951c094ac5183937c52eb1f89d5efc2ddad671cfaee747cbe411027d28a630695a70bd84e6c8f2204baa7816303b598333816bd2e069cdf2d900c25394ac8abc092015c03de91897529f88364c5920c96d79d36adaa49fa4ee79a1acf5eb5cc8c68c2d207e2c00a4be93e9f7f68fbcf3b22641cc46af5b00e468b266253abf616d8a7219eebf4ed0c186804c9aa518e6498a95d27870b09e9a7910d7d2897abb953838358a1c632c883fdf1da7f014a0145b925a4d62389afc2d21d320b72198c16c59816aa7980f35282f8161997479712acfcbf2ae7fc939048221d36c6e5386de18e2e31bc9151bf31caf26bedd9d9a294b1582a0ee1fd54da2b29ef6dbc542e0a402324832ab4a568d02661bee1222aca13a8b8fedf6d1a212c033e3b768116702bbd085431c7df39c570843069c428878590357edc235a0d12653312e04bbf99f98c84e15b59ad2dc98ebfbb620ea25d8800361638225f0f26fa394aef30b30116a967ec0c6a1fd541f82a8091afda6a6d6efeb080c1e0ad26577e4b645ddd36cb9658dae88d305d1fdc55f0015d1b457fa072d4873e8fdb1e81db21d53d8c4cd8c3d14ce58e0a65f6e4f36053c3c0c3243d19046bd8adb80790a527dbc5ede9d8ecdf60de807d0e924b52c138cd1ac03a04d8e88d07c6de29ec091c14d2da49d5d8782121da331473e86bf1bfe45605de793e288bb1aa71066aab84a29256bd7b881523d47ecc983a2aa4255097c5898bb5af93edfa26d5015bb0c6cd798907d7713346c22b48aee272d38248ae714ea7ec5b82561ad2311fd201458f45a4dd86e7a0f108886af9c8d7613617ef0163f18e25aa9ecb792929c4b0889000b9a49cbc9fb16aa12d3cfc54184f17e50144038d3a2a3626f31e2d9fbc165df3f9fcd75d9e99f535022ec91b19bf7c24a033acc8944dbe79ccf224ce84f0ab04273fd6974069d53931a3a69ba7714be5da0f234df46855e054e8299b8a573817dda6b09843956df7a0e2e21d4ff9d91853431e11061482a315f001d434871e69df4e1f24275172d544c130a1c6d40b3bd6b1d7a3d4520217577129eb8a792342940a31d20af3a99f0f4e005da9e561e5237598bed6ee3ee3820a170ba23dcdec74b1b3b461944639305c1596d3b64c83582a758a345624f51be201d7688c69fddc0ff0eceebb2de6d06dfd6cbb7047857b1ff42f680eccd1ac212b73a863059add2efe748ac8971207921c1195f099f9ab6eb43dd55a303e7cf52525392301fc899dfbe7408424a67c509243e5eccd4cfeeeb00b78288506331f06eaa5fd76a09b44f56feaf2d7af4d23b05308b70ccd0c1d5ad8ad1d47b6ed200fc31fa90f5a3102d27b3b1245948561a7259fef448d208a4d87472bb58b647024fa6e040e10f43ebfa76dff924cd8eb7b20dd80bd5e8cf3253fc75ba2921ba21a0ec8828f4e0b8b951041f4e6b816ba41c45a187c8a6434614b24a67aad33d81226a98970af6c1c254981f8fd1d3325244b579ba13038c2e15e670049d7add31e5883b5055d27d3764f707a699a7c34dedbdc2dfa2388b865e81f2f476a9d74033cf6829ede66868d1df503a99b0ab65dcc9ff4581d674e3cc0edd62f6144551a124eabc55b48029f4d416c598c41215d2e3517fc86598aee3d3876d356fea301e68d91f485409a6939dd9136e8ec513ec47d57cfb3f3e0c130160725b15d712c4d57fb83fce70a48b1f42a06f7652f13a403c42e096aeffd06aa22ababfa6f16f60d110b6e2b66ea8828bff298724e5b5fcf493b2106050b85f98ed06491ac17b4c24d508d47a33aeff7a156692b0f923db58fae1b5cc154be223e28cf7085008215ea995ef6c42fc4c9fbd66a6bcb70b2b38709054ea35cd475bb49de59e10956147508ba2e4fb0382e90998b369f03ec4b14d8d41369fbac5544b4139cbc2bd1cb184c504f95bd229f7df496b1e8148eab68ff7a47356fcb4370c00f9a452fad6ce685e37ef2fbe60c09063c8269b6f4a4f58501bcdd6516d59ff13bc307194265fc4a499426ae6b3d0dbbd38289a438005d3a1230768fe7f22a6591d73c1897db709e4dddee2ffea8f9cf5f7505c38eee0e056bb84b077f668fa3fe29441212919a9819f2e3e22e177096168d5e245ef96e5c116cf5c79748d55620b9901ef44b6da68ac3effcce28cf482c7117c35f4bef183e4bbbc96512a7fdbfe0381833ea263ebec528a88e53d624614b2be689b4755ffb1d27f661b4f5a41301b90dbebf58ff3eb42dbd35814ea7a002d1e718e722d9adad207374586887a5224a2e1c83282cc07d6cd0aa8d1c5758ba79462ddc323bc3bbb930373ef1c53b92ed2d96d4d238155532d1953410af79f3d08aa233e2fb256778a04dbdd925e7bdbc182a8858a15071189ed105aa6a222a7a4ffccd9aed35c4d7f7ba647273e2393025bcc927bc2fd289d1e434e8d15d038ecef09f44680b726df17fe0aab28e386d24fa7843e6fa06f472c82dfaca32d85fd582afd60ad817bfb11b3cae843c751e02776d66f8bf51a55acfa283bc6442d185512af1be7154b7a2da03abbd4a60c4127af50b915ec98859d5c684edf56b08cfe66418f2a5889f9b3a87aef425f6950fe98c14c6fe622d1d1c2bc47cfea2b721e97c8dbb1d9cb37577cd019545b71f0285121e51f77094251805dbd8d085ecfe8ccea6723dc062bb3fedf7c50593b0205922a540423bc8c3d5e42569bb2e7247ea76f0336bcbccbd1186dc9ce7482d03da45465c43bd0de56532970355c7ef777b177c7fe6a7532f317a18f136fde2224e443ab209e43f772d8074fdfea3b067c5381c65bb75025ee3f9dee77ce4dc03c743c2c27d44ef52d099e299389080c52600286711e9e1c734dcdebff96c9219ba0fc80d6ea186e6c29dc5c3191ebcddb75f9a6fa142abb71b0eb24ea3fb870d74ad181209cf52fb62d6a0b7c45d9f539812c01d85dee2e3de2131e9fc0e9e2df8e785877d96f7453353cd8e66536472753c38f34c924a11f9bcd3e1df5b24225211c57d2be13d95cd10a32f573bd85de9b70bda007117fd932d6670a44a0206cebe7808618526d6bc0230034e66ce9f7e8f7696a54be414b4caa9cd0855400c5b7787ab19599dffb8d491cab2af3858179c4829812ccfa8950148db8b0925224e70668ec89f91317e477701835166eeac74a894791d8eb76b1baaa550f11216c0fd4163f586a4e16ea766eeeda37ace01ec98c628056398c52e154fb4fa782f2af6aceeebd78d7f15b25ad97c9903deecb86333fab6be68bd8c9ff9facfe4214f0826fa40d3f55a50d102bcdcfa14680605bf36c2d142623c49239b8c22c31977245235b6d40d8c19937dd89007a42e79bc006ca2dbd5457f812fa1f32d9f1029f5a1a4d325d1518f606530e4b5d112c7fe4fd991d78467c24a0487c346a3258eb48dbc8290f47244f0dfd8152f2c2b6c12d1825bd4ef6c35da4a958ad6db192114ee30ab3d73821882eb27c746bd064c8d29a5f970c77faa3b11299f4bde099787744f327baf3422124943d9662f27c114be514e8c81cf618c21a009e2f82331c0ce3f8705e19f585abb4fd7adf1b9718854ad54a8bff4281e2dbeea99271874da9862988c3799ddd9031aaf478420767989fb32ad7577fa16523d27518e0a961ab41d2f1a80097e95d8c03919749c46f05967906ae8cc3ea5de2d4f25d8110ada472dc029c62964550f9ce1372c049f582e33fad78b81872c25d5e7c0521a3f7c7a4873e16b9def6fc5cb6b8bd7d918e64498de20f4de19f6d0c344e526175ab4d28c8c1f400c5b7b8cc871003b1d5cdcd40bb673a700a1edf8281ebc4a0e3721c6515c27adbc7da9925914ebab3eb9dae0cf2200cfbde8e446c38d7bb4295bcad28c370cbdd42eaac458f3ebe5cb7fa2ef98f984babcbfdf06f34b8f65091ee205e13701a1c2ea8a843f5c4b6efd809fcfabead21b10bde2d146aee526215a4fe1159a4d459d3e0016eaf546adab9cdf32bc4379606278bade85a725f7158a13d111c9446a19f6b0bce14bf1651b10bc0123a2a09faaf6919b112dddaa0a0ea3a4ecce723e76ff8831e4cd13d7424c4be81605104116740d58fb086a712e326db739292746d1985c1f9ae8899358e00d533a75efb5d7de1ad2742836322d49011ef90a17d991fcea02ac3274e6b2a9d8ac45e7227708a6ff1b1434586a11db76e8211ddca3020b2850fb31e09acc20b25a225d945840db8f189168aaed18b1ffa314189d1a899b0bde3931abf4178309c5e553cd375db69e22ba0131d8234633d63ff52a14b8ee82edebff56b20612aaa1828f1836d47cd48a889358a31c2ed3975a00706170f68e5c4fcf468d90ef43d57ea3f28b737ead30045fc1be2f085a00fca043219f011f86030f7f33ac46e1f9796a788f6b1c600c0cd7f74d21781ea6582c1001799f2d348f46f6a81f723560d7e920c1c8fcbda35d6dd4560fbcc1e3fe95963b4b591935b7506b55e309523f6ef0372aa53f6f6f39a8fced3033a3b0ff7bf32b9347d397c052d1114065d98e7a107ca39c1ebb71d52d79f101b2e9d8bfa85029b731263a28b39c9f0e9400d3dc6dc1c9328a022a74b704f2000bf3cb1c7232b14fed08b9c620894f038b79fa0dcd99081b21527561ef27b71531fbc148df29328c03a958ae4a00e803eac930ffcffab30209fff34b7d3f79102a1b45d43dee8dcff85bda9102cb809b22e2e10f32f6af247f3cd2b5db032c29d5334d06da44474e388dba32fef58f6f41b64ac8c0ec6cfcd4dc3e0f2673f92e5b3ff63c00f9d5296e18b42d1abd074dac590e7c6e16d339e714a8168fe9dc14582423af2f47b9078fea487c501377206684605bcb5724e6dc63c1e7822abb0855c33f180b5b20ebb2b40c27267d315c4c2bc75dd5cf4a2a5bceadea37ed78153883fb91a526e6f2a716b7139fe0b86b2a8900372f0172f3aaf9ee4a2019ff06e9f15dcfbba8dbb9dd9f3a28e9f54bfd43de7e0f8482a383ed5b608df6cbb6107cbdb223a442c91a27b79c4bebfab136990c609a006f94a4b4aa357bf3b4d70581cb1e022e7beb4fec5b5fb12d0c964ae273eb8ab4dbc0ca9e38768d621b831eb161ad789abe47c91993aafa4250b09fbac7fcb9f489d93a0be0cc4980ac561a170026532a309839237bfa1081eae9a778f6b16665de3237f00c8124fa1fd3253636290e6dbe6eeae74358d67aec6b012104749f53bd345b362b75ab3b549d088599bea97a0c7955ee2b01338bda7d05c5579e6822b583147e1d0958e70eee1b1d6559c7b4a21947d81d6d7f82d511133b9ce051588ce4a1a3e381ab5775ad0a93dbe6e14e6f4d040a4aacb7864a1468b61fde0d2aad5d57f75c6f9679afe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed00000000000000000000000000000000d50b7cdf578f4412a219947e71659c0f00000000000000000000000000000000585506e2fc08284102cd3ce553aa726a29012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f17b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } - }, - "test_exit_code": { - "crisp": 0, - "folded_export": 0, - "enclave_contracts": 0 - } -} diff --git a/circuits/benchmarks/results_secure/integration_summary.json b/circuits/benchmarks/results_secure/integration_summary.json deleted file mode 100644 index 2c13fd863..000000000 --- a/circuits/benchmarks/results_secure/integration_summary.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "integration_test": "test_trbfv_actor", - "multithread": { - "rayon_threads": 13, - "max_simultaneous_rayon_tasks": 1, - "cores_available": 14 - }, - "operation_timings": [ - { - "name": "CalculateDecryptionKey", - "avg_seconds": 0.614281708, - "runs": 3, - "total_seconds": 1.842845126 - }, - { - "name": "CalculateDecryptionShare", - "avg_seconds": 2.121472944, - "runs": 3, - "total_seconds": 6.364418832 - }, - { - "name": "CalculateThresholdDecryption", - "avg_seconds": 1.95707425, - "runs": 1, - "total_seconds": 1.95707425 - }, - { - "name": "GenEsiSss", - "avg_seconds": 0.758238652, - "runs": 3, - "total_seconds": 2.274715957 - }, - { - "name": "GenPkShareAndSkSss", - "avg_seconds": 1.242666944, - "runs": 3, - "total_seconds": 3.728000834 - }, - { - "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 18.979502958, - "runs": 1, - "total_seconds": 18.979502958 - }, - { - "name": "ZkDecryptionAggregation", - "avg_seconds": 48.341644417, - "runs": 1, - "total_seconds": 48.341644417 - }, - { - "name": "ZkDkgAggregation", - "avg_seconds": 20.006914333, - "runs": 1, - "total_seconds": 20.006914333 - }, - { - "name": "ZkDkgShareDecryption", - "avg_seconds": 30.277848645, - "runs": 6, - "total_seconds": 181.667091874 - }, - { - "name": "ZkNodeDkgFold", - "avg_seconds": 78.310650236, - "runs": 3, - "total_seconds": 234.931950708 - }, - { - "name": "ZkPkAggregation", - "avg_seconds": 49.050973916, - "runs": 1, - "total_seconds": 49.050973916 - }, - { - "name": "ZkPkBfv", - "avg_seconds": 3.850818819, - "runs": 3, - "total_seconds": 11.552456458 - }, - { - "name": "ZkPkGeneration", - "avg_seconds": 66.056590278, - "runs": 3, - "total_seconds": 198.169770834 - }, - { - "name": "ZkShareComputation", - "avg_seconds": 52.534038875, - "runs": 6, - "total_seconds": 315.204233251 - }, - { - "name": "ZkShareEncryption", - "avg_seconds": 114.608395854, - "runs": 36, - "total_seconds": 4125.90225075 - }, - { - "name": "ZkThresholdShareDecryption", - "avg_seconds": 251.230740403, - "runs": 3, - "total_seconds": 753.69222121 - }, - { - "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.093863888, - "runs": 3, - "total_seconds": 0.281591666 - }, - { - "name": "ZkVerifyShareProofs", - "avg_seconds": 0.264344016, - "runs": 5, - "total_seconds": 1.321720083 - } - ], - "operation_timings_total_seconds": 5975.269377457, - "timings_seconds": [ - { - "label": "Starting trbfv actor test", - "seconds": 0e-9 - }, - { - "label": "Setup completed", - "seconds": 3.273315 - }, - { - "label": "Committee Setup Completed", - "seconds": 20.279577333 - }, - { - "label": "Committee Finalization Complete", - "seconds": 0.007173125 - }, - { - "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 5158.12970425 - }, - { - "label": "E3Request -> PublicKeyAggregated", - "seconds": 5165.210807791 - }, - { - "label": "Application CT Gen", - "seconds": 7.707659625 - }, - { - "label": "Running FHE Application", - "seconds": 0.0710595 - }, - { - "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 835.140242834 - }, - { - "label": "Entire Test", - "seconds": 6031.697821958 - } - ], - "folded_artifacts": { - "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000821730b5f6c7306ca00000000000000000000000000000000000000000000000027d5a4288c59517c0000000000000000000000000000000000000000000000061a32d74244b4e4cd0000000000000000000000000000000000000000000000000002752ab748caed000000000000000000000000000000000000000000000005a4f0140d4a1c7f7300000000000000000000000000000000000000000000000512ac80c5fa266d0800000000000000000000000000000000000000000000000d400822869689e0ec0000000000000000000000000000000000000000000000000000a3ecc527c1e200000000000000000000000000000000000000000000000564b7881fe1989f5500000000000000000000000000000000000000000000000674ecde0680d8bf4900000000000000000000000000000000000000000000000584cd67f303aef6c30000000000000000000000000000000000000000000000000000623ff53a7e1000000000000000000000000000000000000000000000000b27d89433be674a5400000000000000000000000000000000000000000000000d03b476bc4cac62c900000000000000000000000000000000000000000000000d562dd63dce28dce600000000000000000000000000000000000000000000000000007014317d4d7c2bdca3a8f64dcf8eb8b35863e69e18fd4f152ed73eba57c25990e80b7c74b79013d6c8d4d7310cd110d6e43bb18fc56336d2e4da3740b50e8523d5f9594bcdd812036507369a09143ca35760ebd0173b2c22e17b4dc2980119e4fbf1ed9cf2a9021fe43ba1b07e24480cdba82eac2af02f2e755eb1b42e866b12ef874db8f56729f0b56939196bb391b63f552db3f3c7ec39a1d778f2686df8f01eacb03019c80d8bbf928d3be121e24970b738be38dd033d077d66095a42d4f1494b44eb7df0074e45e1376a363bcf636637b442460155ece719bb40d1fc476322ceb66ab5261818431b676c3ee34900e20139f2ce04bedc6ee159747b931b42f2b22b3d7198079fe5ff134d61e1d32094cc83972db87162171f68aff7e77845fde2854f3b9a0d21b74b20d8c2ec809d9db9e189051c0dd8557ec1661fa1a1b0ce322590aecc23e610a0c95bf8e2f1ec67fad3b50a0d2e04a267e7844e7e78b710b17602724a2b1fd0daf5aaae67ae6e02faeb646dbe547f0c699e2cb1379f50d28f36ba218a1b615a728d5b46c9da5f17803b7b0ddfaa6af8a8d6d945602cfcfdb1a8c2845e10ef52b08e4afe6f9717aa1865a5df615627ece311d2bfccb7831e0631a7271a08ce5712e16458da4091348c134bbbc0f6c546f14c8d9c231ab7975bccb9e02e0c991572aba8f415ac14e2e466eb01533edbb3a35795beea6327e635c6b103740f2b82ceef34e542ab504d3202e68b230920bc61d94d1cf18cde724d06e23a482b31011ca389f49559a044f8bc99e2eceffadc140cae4937d763106987d7fe30299aea525ef4ba58a7d10fddd62430590152bdd9c1fe3f5dd865bb2ce4e22a65086dd1e3b14c653011e7a3c89c4abaa1ed50be316fd87f42661de510e98dc9bf0dddcf46263bc755e1f5bad01ba6f03708b0bb47ea5aad02d6a9052b1fce2f05226a3e2e74566f10fe938081e85af521d530facec93e917a64abe8a781e246752f984676b56b5a9c17fb37ec862c78c596c128c75422712251c9e25f0fa4352a2a7bf93147adfa3937ec9733a4e035e4a297fbb1e9599d1de9a59b617dc808f117e08e894d7ebdc106e7f065fa3a096cecca51bf047ded0439a19e86d1f4a358099e717b59523313b12586636c72a0028ee88c7cbf053fbe465dc0d546b698cf132800fbde649562ed1d1c65b70ad4ba7497d90bbaa8dab5316435f6473614f501bf945798b6e787cba40d1202870cc74db20387287ff9fffb4cdf7a0160ea04251adcb89e4bed874a70342a96386f8ab23147019c7e3dc8a6f2d0f6ee77ea1b1683337f4dbdcb041c320a5df0f41519161ce81427ee42e7e55b381564cef1ff1036c245bd9b7f0292937e19a58c6f917d8bc00ffb2c098590ffa91e35aa88bf0f29c027c75a7685fecf83028b01891bf2582da15f30dbdadf169772056758b112d80f494136784c5e55d2ffd210cab43b12b3b22efff7fcd159f532727da6ec05f1b98a8978a14a941d2489d0948799285fecc08732988a56e66e352362a6be2cc8b3127ad5279b02a55f57f785b7e1db4de806714cb6cfc421b1af185f408d2beec04fcf9b045a9fe4911bab199445b8cabc822f63eda13db7ea709458a02c27135db9d0e4ba34442524e335b54395b3c5d1d19d856c4a813e0fc2303a45b50eb4e75e5e2bb32813c7346b9c9c1c0ef2b8cccfde4c1c8f4370c89efceb6be328a05d97d39aed2e4cc7d7536b34af28247ede4000fcc7b428147375abfdfb5516e986f5197eb977a865b855e9772fff94c52ffdf99286ee66fc4bd6ff9fa6540e0da9efe06b9e2cb8c70f7bed562275ad9485849152717537abaa41b09aef482b701578723527aa01c63295dddd454473177bfd0415c06d3d4e1848d50a58252bf4553e8afc869321e3d7796ba736c8b389b852bfec26444709e9d423192bd117d4a7cb64bb293e7c7033de3dc675267289c8ba6fcc0ca6ce0b9050e79e4e2d12956851fb28b58225eb8c0ebc9a45158c59a5655fdde45136ce3888dd460ddb15888a69073d825555fcd88eb1a3d7f67a4ebd571c54d2d1274518f65805c2382c12086776a4e11771b7d00b480fb1c5a78c300cd815f8039dc45ea68e61c51e130a50b94972ea631e451d8eb55ce9868d50b4e67952699e020fe4a972b44a36115a8998e760ca80bfb4b64576d97e16bf0d1c9b703a106482cf6872973e94520ce2037c03b5c1cf4892fe2c779650c50d7b88b19a1f6f4be7f5c78fb11783571256a19df3f4b96c9fce648218195e60d1c2e4c662d66ccb18a71bd0a9fb8a8b23c3faf51f7aef24669b70e1a4274abc0f66b911c67f15fd970be027f76d1aab14d557ba7d4f268f95afb5068341ba0900e85cc7d322674c057004d6fa17189123eae8a63d39bfc00d3630a696781bc7ecdc4a6b1d7677ca60c64c32d111d3c10f75c8f14135d4faacb046778787802723936036487091e65721e7feaf94a87d10cac1095e8bd1785f16c5e7f0f5fb9fbfb5faa9918ce2a047c7c0debec9fd5f2c41c01fe8b3062e6fb6e6961c09ab4db1da10ab612f4c5a7328a5209dba00950b62695a4af7c7304b3eacd7d23b4de36c705a55605659d07d7bf79a382923d82ab46fbaff077b68f5b592be6cac56d52f95b7f67123c6ef30cce46bb673b92b0ad6bccfde38ae2deb094a64332f2645f4cde17c0a64aa6cf9b79a7ed828fee42cca7f36d7f6b946dfd1e01db6832cb6f4eab7ea793718651913922d4f360d4b21f257a41c05813fdec7e7d01cd94552d86eae1bced28135690ba7c02f94c4102e5902efa5fcb489bba7b608b977362ed8ef889df360bb12d3f6fa737cc23ea410e2c92ffcb685b442e3884384753e48b3af21056bb1eb92dd975c59aa54ed01161b2d1f8b8e48050ebebc4226dad4c4efd3ccba5c481e80a25e903ed9bdd7992355c18cd5fab093b281f89fbcf4c49a728b3339e0a50b7c8f3eaf38c0a45a241cd2d8c6780c99d0506d6db1d8b009aac9b761cb438e732bebc6c473618f58a526589895742bb0bc9bf2157b0ac83056acd061226b5155c952999f617205e6231dd54dfe603fac9886d0279e7a02a79d39fc16d9590496af4854499544f0d61623b4c14179daa63917368e6315d7c8044998be25f1fff60ff99cebeaf702094322d12ecbd646a3e725894e99bfa905064f065a4bb9ecf069150adf4f6f234c7203c810c6f79c786b859baf6db1ce8395ded182bfce2fd1385b1fae68ce21305026844979fc3f6f15e3f01a641e070dac977d4c4e8251de5b25aeba717b43bcd322a34d4d3c3c7f307f3cbea1f6c4ff7736b1fcbca1b856a1d39a1de2a09e2ff306d5df092ac06ba92c3a7a18fa39a0a90e5b2c1d4d8645f0ffca3717f752afc42eb754439548876e6b43c3a5d94e1bf5abf0e7f9b5a6686050cbba01a352ecc21598da0ad02780190cf24cdcbc430d5aedfc4770abe9c6863cfeef593d95d28b1b050913d0e34830cae0602973282d94fec6205ec8756b4d696623c67d33e55e05d1cce77fdac4c7fdcfaee946a7baed22eb73fb1af5cea27644c13e2e249b6a0fce2f7340ef3aa4e2fa39063e6c8ed748a9d29f33a38f4b50e12d60c03433b81a80e41a565ac3c7013a1e851151847f3bb22ebefd9ff335ff703efe6189df8611dd23fff4444e25494a43eddb53fd61199cd4c1c04def81d6f77187f8f5e3da1a1129ba1faa492ccfa688a8fab7566278f412deabe1f69458de9a4991000314172a67e9eec385ebcc15e24e8cd28e117a2e885e418a5eff75fa6e0af53d4782282f17a9691bb7192b5b670ab7a577b331366962dd0a36346f28b8f56ec241ea0b6552eb0d11d855088e95a118fd271c8a3a1922ea0bfaf0afe9ea5c758e20492391a1721c25d590aeee4199f47b8e5c32ffea485cde82f7392674c9de1aeb3c183f9916e9789575f1b0b461ed9905831051ce8b19952d6c331efbf48b7dbf4614a5b15d0e87af9e76b285fd9ecce3107a983c726ae5d3d6ec6e2baeb34a9e390ee6e7510fc9579db80f9d8ac57e1cc886eaa4d2e4afe8430ff03b6c6f3aec5b2ebfab036e1b2efe026beca2aaeca416a017afbbf80ccbb2e72791f9cdc6f6b505b1dae159a08ee04351f5f2f240edbe45973e734d98f6f4cc9e2b7da8da6a110f7e97eed170300d1f8541ad1e1cb3bf6b3863a4cb490cb5c39e6d4faae2f6ea0082ec6630d36696ea41f90d0d64da3c8ff4facd1cec120e8736509315d426770883f14157bd101f004e3e63407103842843b512eeef44a77743bcefe483286303ab560fa84c14a40002cd93f22ece518b999cb3121c7e1df64eca2069230193028e8d3507fd7d7be0e586300d994405ce6dd9895c747b49f51f20d494c916c808ec85a1e118a4ba5f6448c66d7ebbbe036e81080e7fb9d38cd1ac591d5667dd060cdc04db6517473b7814ad9d757fca4904ebcbc818ca1f22ecf674dcc0c8ec1bb2763dba05f118fcf7500e370108e9004dc46a567294751b7ca51e51229b22220d0d21392e57177d6e2671cf3e954eceaca2cf87918287bfc1f6bf533708943011f277db9e4f9d9ce32ae3f3a1a3343d87d3f95b52d768e4848a92aff7dfed1aead60fddbfb11590d84445c4e75305bb2313c20df2f95eab7e64eb085ee372282e3acefad52cb23bf8855d760a5ded264e7cf806f650248c6db9c54b48e80709642a094184762433ca772b01c277a6fbf700c2046fac1e7c45852dc4c127ec0307dde473f72467a910331f49b44d665488a4c88895342608a78ef07cead2c305ad0fb767e03b49b8e665584ecbb8651dcafc1b5c116b33ba927fa8d15b98f5214a6f4c52b2e56646a8d9cefa2258846c3de653e2297a4051385c199d278a5926a6a8f3f0356900b0f226530e88bb4714c63d444b3dd57e11227770c75d1d2f225236f50e50cf07d29626531dcab3db32b7c1a3af3e43febe274500247334660014e70f3347b1f65c35c55630d4db54848774e2c140eb7c7dcba10652028cc818c7a328564594c77c3ca4d6cf9b0ce46a65cd4cf70a6820eabf65f863291f0f0c89ae9929974dae20ebeb9d518dd20b2795765cf0b0addfc027806a744965d41d9fe3385a89d328e2f8a5dd616160cda986b7d7ab9ec9543d99fd0f70ca38771d00e76228c39527af15a012f227bff39d231a2682a9bd9c4b45ca1fc2b260aa250577f8fa880edd4a07dd8115511301135921bbc68b53bd2375c7635fd9dee70dfc0e14e099d417eb4d6035da9ff7a9f647f84df2437aa344deb5737717058d0fff3c23d38e8d2548ee3ca6dfb23b0c4dfd22cc50c86134291cda45e0131e1917765092935098bb1a016aad380ecf5edc52dd0f95e566d538900955bcacc4342d16f512ab3e6aac573cb9a03082605ae8a4850e455b5d1641c03a37b0cf6ff90215793fd21ee0fc330133beaa6de3b5d59c40755ce8d0cd2c434b9a228fb79815ac089a2735a487f497d5758fa303de789f3ae7a09298147bea45a9c69ce19e2d058fefcd8ca169e2ba7760c96e7af51de2a28cde2d2174ad0a910dbb954f6e2fab7246831353872aec29ee4df899bca2eb5001464c97b1cce8c138fcc94ec40c8e76d15d58b5353e6b3f9ffc5b871ea1d2cc1fc1ab411671eb7d4f80c28b051dc71f34aaceee78f29a56e4529ca51c856472af3c41d587c25a3bb5ddd190062840ac3eb0fe54d3b1f28f63b89977405cd88ca9c73df613c7aaadc6965661c70747b5fc78ce643b6811e78b86b7f68736e2b356a934001d2ea264a89cfbe99a12dc049b6f9ec05d04083fd34b5647bd585f1be04af90aec97ea1e1ee9d42b841d7f31c6fd3853b6c12510c173a710adacac5b23fee0d5586900c83d9711955a1d5d41d21d27fee28b4989409d645d05feda97d3b12b1bcf31e156c75bed1d3d299392602de76f62b9103cce850a2b5a875ef961bd2f58321bb605928c80a122147f4095ff4fc0c4772cc9eb0052f677550724a61d0bf0d328fc6116e13e20ba1a7ab9f9f8506aaf4d953b7c13ada2220f7f6f4695fffeb8a712b5cac9f185ad15a710203071669acc41cfcb817ac9aa6d2ed7c763d9de8b8c7ac7374b0896db120a5f2e718e228bd07965ea30f302417d9f9ab3e01991d4090fd2b0920194ab038128867db1f90f4a79b4586f7602b80324cbacb580b34263c724430b9b91632d8d497956dc8231e14a4d8a202676f3b8a272bb1e26122f025c4f8f78d5c72c21b998aebd00ed34be2d76e4a43006fe81588d6b23eb5bf4c3367285c9a9ef2927a7877682d3af2d5e33c05808479f4521b3a17168e6c97bdbddb7770f93d2f01b08b7b99d331e78b71e84cb19252fb9f9f1ac65e844728db5722e55ab95b9f41edcecb4f31deb949896cd3db5cd0dc2ef15e4ba0eadb9bbe2657b321e467ae20c88064c8c15e19067e28939f8ee13304d7283a98fb49625609483ae02f2a30d160db7d5914120348062db5917327da32d027a758811aa7fd1c21b6a1dd8c75e09d22df3f7027626e95fcb651e8b7559471fcb5ffb9162d550aa82338c51cb0f02c653bb8a2b59c2959f293a4973748870484c05d4ff511fa666604692f73cbe282d4e0e243d6d9ba21e950ac205857f6d41ba6073034f315ef27929d5dba83205f192e83a1e5003de8d1c38c6dcfa2593bba4898090ff610b0890515f9bd44d06d0e8c2d93df6d36626472b3ceba0994cc76cafb660d6a692e7bf0559db1ed02fc0cb1865d7d25f89fb247bfa0e1a3fdae33f97abf5ae7d9e35033050445f73244d228055862735caebee5db8a119a9d8d11a7035555ada83e299f3387abf5f0000dea04c2732cab2470e3e04688d9f1dedd763e036634d28ba9f8e2c5240ca271898be20fb585b49b60139770919a34305e52be70f0d50192770fc02517f652e1e7ce1906dc26a8fbde6939f69b2c0645b8cb4a3954ad2b43918305a27046603a85deac0c770c7e523137f35e3000a0bf96b8c329525f65957437b757c262a2bc6a9c8527c1359d8e598afc781fc2e7ed7501a730554c58d2a552329f99d5c2635fd5fa08d5535ca03c6ea08662a9c52fbc5959b1a998374ed3f624bab2b5b2c33c8f5aeb856586abd239d13286104a2e90e79983c92ed579bfd5576668f7214523b4f53f4bee84986ad86798ec100d603f863f155fdfd2fd2cd72f5676a852c49103b1c0c67f55c4af21fafed6d1d9ed79f863c9807eb055b1777945f8d4f1e0db4f8234739e50bb75bc8fde5af7ab0688ff97d67209fce8a9b39e7b4d05d25548e0d72bccaffb3b604d3b33ce22039ed319792b1fa674d396a6c9786dcac294231be2234a234be2524f2f7dae91c55e58074b0503b24530a0c6e6297f19b0e695dacbd609ce9ca7cc5b2511ba837f4eca002f7102967d35389efb30000cb181f2a3daffd5d7f9915bf8dd1de5d4e7659638e9584d13714469a9ba5d37d7313784f05375e9c5e34ef8219292be23da93ad73869d0bc71516e4393f5a782062975871276d39f2f41d36b81ebbc221648befd2dd8b9ae5610fabeaf54f1947f17dbe4490a05d99f3ac74e0d537673afb9d993294d2aed3e7e293ea0d280ceb019e6f0704f9b34dd1a8da3dc616c365e09b09075d2fef5b1ede4a3bba6df095b1390ce5a31053ffdb83cb05b9209d37592156cb464012744e90748e708378d9c04416b2fd032528cc00e516f1fa6bb6bead7b0d0a69d97e0a14e3b40ada5065b101efc0f1f4cd612b723e18127513fa2b82d5f5cdeea70c60581191b3c7cd81f1cf121cc958060e951b4e6c76f2d557bdddd63f9f51676352d04e0174aa85a900b4a92fc1ddfd06e8006f1d78401e5f7390ace2e425bed97aec36ff7e00d2f6525871edfb56c38063860c618feb09c8748f76f88ea04b6ed77e5421025254d1b2ed05884ee40dd3e9536f678676913dc230188ced021bcb986b6b18b475fe0ff25ca62e1660243f9d9b164881c2398ee00b789bc8c4444d3682420cb869165b30e224323381cb5cc73620a7a96119458441256a6229f2bd621cb86ecc29165db17aeae0846231e6b67942cec4406b326de3bf6da88d906298be18f50763420171db37eb6250875d716390e1399175ee4eec8b0bd26efb179532d87783746b5521a5304ec07e9daa765361558c3187a2073c503c3981ea7992d9c60bcf15ed2832f492d65ba407662a090ab3ef91cfcdf450a588b143f4cbf6fa16010ea41310c305c41056c1e5d407b1eeff592b7fd4f7321aa7e2be12587c37faba2dae3b5050e6c1530a67f22db6c8febe5f34295ef550fc8bbc338a40623dc6065c708338604f699048cd976fdb44878c7b1059cd85f4b91b964b8df3bf9c70f3c2befd9322551cd9f2b50fe2d058916efe06f883fde51721cc8523939a3350133f533f1810524dd023854b3ae2592e3c34de948a860ca68eab2e7515f901ba4ef130450802767ddf18f04412c57ea96eb43689d7148daf50f18621a2a2f4aa76ff98011bb12d62e69c1c4d4a8baae33a8117b84857e317f8562da97c5769136574dccc90706388d13056cff5c7444a3beacfb4110d60cb4a41e70cfcc8de69c72a693c3c22cc4374fcd2cdef089cdfe6a0c2a0e8f8acf27d8e36b650e13a8d51308a25a6829c36bb3cf8ad207df79717b3a3fc45a0dd3b5db984b8293dbaf91f785fe0fcc0c2b38ec1232d147cc42ac3808a34322fbb5f8306303cffb36614394275526e41f473251fd02c680ab57b99de93a2ef421ab91e4426b4c99f3c6d03504de269b275d2cf9faa91d300dc7bbdb3dda6da71347133816be7b051d48a33903efbf4407f90419f1332cc03e9fd8c41e24a26c8f42481b2e9fed3327ce1798263434e51fb358e785403ba67331bfb0f3133ead7225bd117acbf86d18a8703e1d3ebaef2281f87c90eee0cdd19c503c9e39020cf0a6f0793d93dfc23d0167592855aa6516320c766eb5089fe5089ff9c2a5f43622cca3fa745cf3e61a9ace7b7e1c1efe1bdee1c9bf8561b342a2cacb2584c6ae87cbc23ca7f21561483dc48e65ed74d70e36d273946c8af8fbfac07b8067c7537725fb15dd74d6fd8e06d9560d04fe9e0bb9cc66a552723894d500bcc67d9dbbbb2f7def521d214cde49257da5ebba58127ac3ddcf681962da25f22c4452c92bb8643d2d51cd72af07b36ec40933aded2a7ca13c7f42fad892ec2a215c0b050a497585d5acbef59321362e9ebc54f7d7299414c10a9a83fc9beb917a3272cbfffa4e7da3fe84a0f10dabec1f7e5a897e0962a0e842037b4604c054cd61aac3a07fc1c81f3a72e51e2ec990d4db00b9eb14f32933b1cbd476385d2d315eb05da63cf9aa7392e86f8789f1eecdc9aae8fc14541b5d0cf84ceb4c04ef83b692a470b986b75ae8fbd52ed926f7c50a3f0a5c0025385e05933854303b5fdd3eb968d912173130bf0b081ba8ac3f93cdefc9a8143fa61d6377db816b1367c023307d2fab251d5ca6e846a272a4577a7fc82d0901acf57ea102113af63c159c1f5d0d91078ffe6f22541bdec846436cb3729c8126556cf495ef535047d5bdf6cdb5aff1c08ace3e8ba81eecaaee4bf99b80bf98026d93bd1b87cf7eb21a4085647627e2b0b2504884d459721c7b979153b14d161ecf00bf75bf12b4c944c0096897bc8c53526c746135500931ec160fb77114a317c0ab237646eff1345dd26ad855f8d7f7228ad671ffa4793f582b208bcd2dbb018fafa828663cf93e8ebac379ed326fcd502f1b27bffe1912716dec34b4189d2949271b39f8384eaaa45e64fcc800b5f805dcfe8e9986c26d7541bf962d1166044483913e751c75bae08db36edb57973712fba0c80d6813f1e051c2af60ef6e117a84999eb5ede01635927eed50f201635f19e297f45c406e75470fbfe8a40f0db953affc8f61b592b5b0bbf515eede750bc8a0593edcaf5959a5445a1ae0c1130526a514a02445e3a6978f1849f50a7c23db9340d42577892f4d385814e49b151466ff20534e74439acf791d130b0f765f9c9b2ab66fda9098c7771834d71e0ddad7d678af5183635414a7ee104cf52afc1dd2ce8fdff5ba21534f4d73667501ddc7df289a79ef1a336f930cdbe947883fab991b26adf61eb7837d582b8e9d15ec335ab0809126826fb09abb2da87e9aedca01dcf1a662b74cc555fab8b65c013fbe0dff90977f2a74360eb7fb0e75c59408389757e17ad071d76c112ffbc4014b0ca129f864a2489339ed260cf0796380943a2618f3b13e66649fb04104ff15197fafa3b047eff947a782bea063ee95bc5b8cddbc7db60661db0e6a47be4619fe21e5918752a3f48836a6630ac27392a10fe2b6a6b292903709ee2f82c75e0d667d9b51d5ce9ff23fd6c7f2c5a87f087748824f9bb170c33a51a704be5f042a94345fd5fa26e80dcc485aa26fa6c6061d6cafe422caf1eac52f8405a6c0aa2bff08666e2efbca4f1baed7f5532cb579708baeb3555ae6b984fab00610901e1ba452a848424b71e55829122932331f5306b62a5beb24c70237eb9c0e0207591d783524c1a28c1fe61f050b6bc6c300011f151959e387c0125b787cdbd2c7241f837fb79f84889b30cc8e5e84cb49a895585e78ca301052c5df2a4fc873db770367667202002946d7dfbf953f113a006ad4837ea696baa9272c58c9af1d6d8e1f0f21da94147cbaf178fb414d0a77e22c33c3fda3d0be7ebcfb9d95daed750008c9a1d8ddc601df5867b8277024e8c2f447a739164cc63ea44c03ad55282f181cf08f3933494d3938e6937267d1f54289a4e1f19d16abd449ee8372fce0834b212bfd7c33276b09f5ad9221383f3bea85ff25427d5deb17e7a510d4104e32aa160ae05861837210115dca42c986c81002ca19b8d3645d1b473134a160d34ee3261287fd51ed3dd9518c3ca8a42d99bc095a056a3a51411fe822eed25ac7b9392e319e63cfbdfa8c25b357407e9f6e2e249f9a75b5fc17b8940b10c7a8bdc0c80ce7c78c5a7da26bef638318da682b033778c232ce708d33e025782dd268861c06d6ea5cd80d15066128b7b0b5d5b6f51f240ea176d0580ef7b02eee47a5cce712042e07964c9c052cedc831ad39cb143a888041c8aeca71ddda00e103d609160b7ed571aa0f7dc432de1024a9572b2f46029fe8563ff2fe6046cfbc4589099a139ce636ae588318153e65aeb57232762e7be53e82ec3c6f46ee4f923625378f183f5c778c1d9420a1523b518c7b970ba291de12f4ee0d3abc0daa36f7faf05901731bb21c7b0c1e3fa46a76e005632f8eba62e811233000cd2539f44b06619c0391e67d0b33b06bdfc98775458a4293c9515b87e5c25f6cb6d29918837096890ed9428f0cbbdcb8eb0edc385318bb3e61aebb5774cb91852ccceceae70c38cc2056a75e1d63ae67c774a818ad111bf39acba607cbe4864fe74d45aaee92912e237f27cb4f8d40f7029eca2bae4caef946b9549348e37161a6e56168328e03490e955c18c7bf6cb2ed0f8a4c94bc635ba90724e62b29de6d7fcb2249d3951b32012abdacd304147bf0b87a684c56a84e4654a1fdb1c2109d4210f7e9031f3ff9280148dad4fe9dac49cc6815a9d2910e9676cb332990e6564537f5a787e126bb154cfff073cfacd39e74a57c4d0d382155eea6093b96ee0e0bc1d204f58742131e755596903ab9eeb3e947677bf81b62c73f504acb1adc7e4dee30c034ab75790ebae3861e8c094437ac0d5cfefa3b3b0dd75c18b443e46060bb69f122660a7f0d8147ccd093f2aa6b1ad705f3791229891faa6f27122529f42e149eae990fde118d8dd0656f0fa9f5267beb6ccc40fdbbcbf34de723c173979387831ba95cc811e1c79446cb200b31e31f3200a287f582be616f920f3b24590529f1abea7c180a6ce5ab8515f5498960440ddf5000079fa233245d1df8fd688ade85739eda4906703d70e7215b65746facfcfe1aa1ab8c7c817b137661d535ba08aa63aaf44f2b87690dc15322b2c5cb26d729a557bf30748c7dc60c36a9610c9e758330ee4e1efec3393675cfce7c64da0c1fda8ecd235b8c76e3681cff99daf8473c686d1f0bf6aabadc7acc20b88f11869b498b3d839f679d443073ce6ef84512ebed8821286504bf8db57ddaa7f07b0ef748604de3498c5a8be6c3b5b8121515bb8306782ca30bf541db60bd1eaa09ed005970e3c80a2c7c143f6504d10b208ca352b8880e915045e50f5ae5d45f7401b0f70076e20ca6635e89bd950b832ff0aaf0236e206fc268a7f893c058c73030c70f4c7deb51ed388a3bee3aa16534d5319836be14f0addc9788e212172376c5f8cfae54ee2cd8792ae628061ce47cc1a22869761305fc7a5369bbe86b6aacfeb72427f397e40e8b901f5b3e292d7d29160a11bb05979b806f46ca206ada7f1ac05c54c4f49cfadcc39cba3e49e3ccc263ff77c319ceb2c976e8f0ed37f6a73c371cfd76d1b82af0a1e58a6f8e6c0068076db9c229f59e929d9547d2de2db72aa9b89fd37dc41561ab1809614d396d06eb5689f00b058cb17c2b3cf46fc636066b4632a5023dc55731dcc76db3dc1a0a0ec84c900a469a7d17720805a3c2bc7ff493a2df926d49a394a7a0c45c7620f051b2ef14160d19492657bb67d1bef596d29b79d29a4babaa5428795879ef66d3aa7dfd7807592edaf9d568afc173e452e112179d987725eeb41f1182a9f2bf32db42b63b1898abae8d596b33efe7b6607af0079dac5d356bd7cf13d10d0a4e0768f2114c25ce2889ce6c34db05ff06fbfa4b955b018546e27cd0bbfb2bc0cffa0792cb5823bab8a977dfd68e70f068ad03ee38aae0e7d49a8957c3878d643e6d0f65fcec272c725699c16e846c8638f5cec0fa2d981165b3bcaa7602938f4a2149ed71e10cc23a9c5e7d0a3f5461709b23cca9f4eef68bfd06a6fd5e505e756ab4c40a8e2859e0f20337582b5f4556a47c503934f1b8ce4ba7b27a31560d3809029ea2d2067381928499d6943b040cef2cfc40f7025ab51d66d46db1eaf2dba55ffb9d472f04933ae5c2edea96a0468675bdd6c86d3446009068ea089303a1b9d053a8841d98c5b5a491cc1f8e8e260a3655fd3a6b3dbc20beb8e874272a67260b49afb0213e712579743fb62f8ac169ab6500175cc164c2a5caccaa675159759f708016141c4e101152c0e8e55842f9bad78fa3d8c4ed129f91d6450d6c06e85338347f0f6a91a32ba22a390cff1ceb374f5e2b6745e8905f0866029a4c2f9ea7299cf31ea5b0b47ee4d6329b1ba0f1ed46fb0be938a45a2db24560edd0dafb2f1d5526073075b6b2bb62555d44907340a599754fb868566a768b458d6f6b038866a2512c37b7d743fe40490af6e2267fa03f03e2a8f9b6d95650221eaf0b4c87ccf6d505125e1dcd88c3be809632f750178fb8b6bae40b12c686a256feeb481b20ec7a2024b45fd5c00f5608c9cca96508b2425fa584455c1c79dea9c947c13e54f2bd0b77f70ae6e1aadb54cd3af5dcbd773c96a36c60a6e94d41f051284397b36e4102431a5c1227d7a2e5eb432dcc11f0fd5884c17eecfe66a1ce2a22b0419b751d117401dabda490ef21888c13c136e09941774ff0b070e9b24e762ea7ff7684822e8d26105cc2ca0b833708f06574954f6b582e8053824a60dbf179e50db6f55c069244550b03c11e5cd22653891fe5064198a330115698a1d852f57c32eb855423c9db7c5aa271f155577ae09f6e5a7e32c2aff1f5150feba43b795cc2584c07001cb655353eec53d999060ef4380640747f65e57196198444330db72f8081f5202a4f8e4f598610629f96fdfd5734ff55a7f4031c8538f9fe80d1dfb973e8d61beb98f1f854d9783b319368ae6f9a015484bbad829d30cca00981f840ec563a2e24c11cfd9977d1f787fb0a395ba4b1acfdbf6f2bafee57074e5328affeb76204ba93ee5b82ed1dded622a843db90a4d19c0b4ef46867161b3f6525493b3a3e285408073fd97e2f53fdb86c06e9845e8d410462857a2b9243b348ad17b662cf06686483db4b559d400f33726b232420a522d3a4a63376d60b56f5ee7cac6688203272656b35c2a109ed301e8a298fb397d7b66d22b4867b9806269b28a5c16d2c366e20f6c78ecef995bf41e82d6fc7cc578e293bd917b60d9bd6d1dedd21bd12b872b9d3e443055b534b753d81d39377c29f810995cf6238b69779a358fb6208eadcd48848b22b262ad65f2682172834b865f293d2fc5cc95538f242b21cd12261023dcc253992cbc1c548d996c675b14dac9b530fcc0bf78fb90bad6027e52502201fd2f781fa9dcf4da5c91b6706f2eda965b330521737df12d13d350c2506a779924f2bfd8c634cd73c84811da8534e0b14ee42996d6ed01f879d4d858605adbb356eec9115a7e0a689e0de4c27be333631fcfa9fec8d02744e206dde6110f97142186a44a6f4f220b1295b4e1b0385b0826984a145ac28bcf89a9c3627084ef803b0cae77c7eb29fe2d6ff15e3762928f2f1100f4a8b59adeb81a445e70c54d7b8453eb39ec2e05e612d1c0884eab9b6d8792606e5ae8627d0f4d44beb2d4306f31b6cdc617da0f5374ae79900cd9b9f693debb14d59889d5b802fb8471c0a414faa8abb86d379cf24a883de065656be4205dd39718b1dab3ef93ba2e5107d2c79ae5b564bdaf2971a6ea04febbcf5e2c7b3e3743d8b37631ed2ebfc2a116cc9f8b27037cb4d7bd4078692ed74caa2a1ae3458d1eaef27cf48870bd95a", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c91109676183000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000008f26088c208eab68279f6f45f234d271000000000000000000000000000000008ec0f15e98d9058c0e708be73acd2b9d2197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f2a318f2fc85748ea90c49625631654288c77791d8dd0792e201c748ca9863ac417b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e2bad40c7a2fc975f2e4811607e4d5fb72361dd1af1bb2364be799270aab5c6f623db21355d312ad82ff6334e39518d7aec226005c56d124daf506a44749cf370" - }, - "decryption_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000006443e4dd7cafbc6fc000000000000000000000000000000000000000000000009a5b11556957a2fac00000000000000000000000000000000000000000000000481d8c87ec8d9e44400000000000000000000000000000000000000000000000000027e3c69ce5a8800000000000000000000000000000000000000000000000c89919183396d2ee500000000000000000000000000000000000000000000000156b5f6351b4e1677000000000000000000000000000000000000000000000003fa278d2c3f3cc83b000000000000000000000000000000000000000000000000000257547a6577fe00000000000000000000000000000000000000000000000a7acecc082a3669e1000000000000000000000000000000000000000000000004e4ee60a9610ccb0100000000000000000000000000000000000000000000000139a7b0dc64569eb80000000000000000000000000000000000000000000000000002797aa349525b000000000000000000000000000000000000000000000000e4efb10408e5e22300000000000000000000000000000000000000000000000565dff2497b05203c00000000000000000000000000000000000000000000000c3ee606bf31b8fa98000000000000000000000000000000000000000000000000000144da9a140bb701dfc1ca1fdf6f4d4992fcf7bebfab3317fc2caf844b02fb8ac4436d5adea45f18531115019e88ab0ae6f13233c1014519feb8d5364b2b488e9d836182eba9d800f3b9f16b517a82e074902c3c99bf34e7be3eafddb913ebb2c8d05595a159bb2a543541d15947ed4f1c1620e84108ea3ec32503ad7d073162c30b94474e387c0cdcb845177847e28da58f2486d85540c5484ae86f2294cb46b4cb84d0aa3609083d483a8b920f898636ed7798e980729ee4ba163dbecdce8b02670604ec9b920e6c4aa8dca3ce558f7cec8fb7f4bfa1b0d4371b18bc835220613a1409fa080f120c539f947fdfeabe4f2a16449a964098c614d6fc20e3e80d0403f0241f5d8a13147d749da88cd2a0d8266d4380e5070e0fcf87e2e312a3cdb01409afb8ce082549d88678d5f866e68a4d0281b41a6b539a157e18a18aa47a9d801ba82c15b4285ed10e2b52a839fd1fb5a55f8de22855f4af96518bcaccc2dbce494a296ebe26360d30cd1d486f49978b029766d49825f3c54d91bd255e22e279a69caf0742070495855195395e1742793b60d1378c09dc9c7053216512cbd2452209ec4be4071c5a521b696e975b1060278c4f77f59bb6ef505a7223d5245c2faccf2d7a7b17bc6a08396d8036223bd5f0ae7ed2f2891a3c4e8846b79bd36ade7b09fd6627114aa182ae1731eb6b9cf19b8b841c3c2b6d8056888915d6f3833646d375991e22759e6ca02fd6241ed37565eeabe5d1c1ed7734680293580c5485cae983d60911a42e17e7646d7d5bf1b3633d1bf857ea6d0d6dd6ac3947070d93be4e391a921af9366211aa3384aeeca7a871208ff44293b9bc62fc7a31c346b3bc0a41d8ec2faba055c65299d4680c399879ad3b09b91ca75c154614e7c2365c05d9e1e1bf0312970f67ebf20490f3b18aa7c84bfbf44044a62091158a689ffb7eff443a0d006f17dce991bb9008594904f9599b39252e57e6a7ab50656c9e5a8df72b58d51b7c9f4435e6f52a478dad1fa0f66a4117c91477773fef118f1a5b2e2246a83126b004cc496b97eaeb0939d7fe1fe9df4c45268c13f7acd87f8d7a7251397537066d564a275044ff3e9013c7416550403fd3b89f31931a1c9bc4c78890d6039319c12ba5bf473841715e3058c08b58f6c885980ccb7196c78fbab8bfc448f50b23e59de65950bb9e7523d7c99364b96ea7e2b23207d02a56f9a6ba47ffe607b0110784790f007d11c1a16dfc27e06e1ca443bf4740ab87fe60bd2e4719005c8706dac42041fbc232faedc6e10c6a6f75b3fbc206b8f7c1648c1104fa3deecec91ef7ed44600c3779a8af114f9e25af821788f45bd741c89b36e53c6e5e6e983e23f372c640298eb2ca3865a651b2add2714020d6de218a78a66e53d5eb9c037921c9f13da3795f03f8a780a09119c28b795e6e6b97d67250bc77c40a81ca7253285c8d29c99aecf5b4aec5316f45c7934a7c13e79ba5f526153174eac6f0fa7f1d5423429dd2204d5a0749d4f1d72ded1463077de54e2914170e854d559814782791a449f5831160a02643aab224d9ccadbd7c12366cbe5895233711003e08701b69ae17495eb9c96cb4741fa95d3a2342926bdd5a796de050b055178f9e91ba2582ad1dda122e9168349f910fba42f2389be4035b6a4a0776ec380eab4a7e1818da3933777a20728cd32e8aeb3f6dc1c954b5e7ee588996ffb14ff769561b1427200b52b53eb0c2aeeed7d7ddea931eb9dcd23c5652b768cf3fbefce53500b8030754c5c1ca1108f3ed46afb942bfc01b032b26a8ba44e62c0365ca4033b1b219a80f8a2ca3ff9ad952bbfb2493969d7eaa90cc4aadbcbf36dc76a0c848bbed2978369892c56259b660b0a07ee28fba8b592f129d029bb31b80535d8003742403319cbb781f5e4270e2aac914ced8a3fbbaaa3d9f1e834bad9b9f07b44814681c3e7cf0859f312e121eee65169a441b1dadc576870842c917a36a744b4f4da91c33208fd45e9c462ef67ea2f432b555a46ded01dc8ac89fafc61142efa714650817710edcc68e99b475b7ab403a6de405f94656a27d39fe369e561b26a5457c2921ad7d3ef554ad5330d553e322f95d5c40c50efce4a009493e71875eb2182d0f82765a780330127718248a51508491c0d2bd80766fc4007dc818073c337f882d78d25f805d23f12d1db526fca5ffb5be0e54a6ec25e46bbda65b6bf5aec648184761fa18ff033b31c97d42fd3a43ff3f7ecaea4d304d336e0e917a0d2e4425050be6d7fb830cf4562eb87b213a5d6a0dfe7b3d63f8e11e3b1efa9411183811071ad9e3d533211eb93739574879ef787af1314cbf7fb4e874c3ce87ebf8c3f60f93f0b8ef56a6f27811b193c26971d3e773e76e68c5daf56140ba20f67be6242773bd78452b12738a0ec3b6204e93c9377acc0358971d74ac9bfeaf9245d9421a7d20b5a4a2ecd40e4d23d71ca5a7e131b932acc89bfbd6866b38719ce78d212cf028cef1628129d642240f4b2730357494ac27446f5e56c0978e0c2fc239ee112666f0bee00ed47f0164d22a5215d5db82f3ab6cf249bb50703d3dc27d71423028a4f72123edc079901ef7ccb56bef61f6591b0477b23cb07795773f20e2bb12e5e0ef459fea65851cfcc4b387fc42cd5e71bc089a7f4381c913855a4f2f1f0106cae843799f89fa4b279a364f633a37e4b2640c69857e61279dd803ec5c6d039606c9c10584fac7c1d5b10de7b25738d89fc5569329afd79e885fdb953cad2cc05964e713e4c71e4c07ff1cd10c406ff55cbd851532a4839738d7227ed257241f748badd25148e8ca89366948ebeaee84d613987f693e0d213aaa594db3021fe702e566a8ad2d75ae4f812859398669a51eabc475fa34a0d77bb9611932a904ba84084825294979e0ede81171919e8bbab7354cd01123a8d61d9548b311ce05d765d5f5322a49e084b9796079b3196f5a5ef6761db7007dac93649c4a387d02a34ad68acf136903543014ec4b3d4a3fe900211ea22ea18eae5100b319d329034f88cb7f95f41dded0a0ad75b68c71278a61b51c19c035158fbb8259d36bd505735e825c6b8f845e8845155cb93ba122d3e98c3db8da44666609cf7192ff0d15d728de03a78427d9b166b5ad8555e4b65afb3244bd53993eb496c5cd50833d22341df3b6674d72bb8271c4b82e7f03740f15b18a7ce3fc8943f81f826fc8fd227bf5ff41b0989a237271e140ec6ba99a0c2c913033c1dfa00f9613114733a42111810a91eebbb5bea13246518438196d97dabf006219b2203cbaf73568d7f8077e01b07756ea1d188ab0a2d7d2b901634618048e281da916256acd484950b7175c097adab59b620eb4718fc51493f0566c9e30124ed27baae31fcb691fce092acd8a3e8ea251794afe8d35d55ad7037ee89476c111e04610f219a1aee124c22ac4339cb28af586a14d986b328dcbc25636df3bd150a77cea7298745cb7fd6b2ef63e7413dcaf3505f98d6569f1e4c8f3d5082903c2a15cacd73e75fd8df54e2307f9e71217ebbcf22bc61abcb8690a22409214cd7847da52e761f7ced049a31c763c92153a0b2904739905c9cb26452f4f573ea8f9a7740b72e854a50ec596115078a8b11c57010a27a043652ff97f51ca448f30614810e3765b8a4149a63e291d1683a4b9a0b964f6ffece7dfb0cdf1c4ce4f0fc04be99672261f50284503148cb9351ff4662a49ec4803560cfbc9165ad379fc90ce53beb5ab320807a8d91d0a10934a2b9e4363934b89c786d314b1a242e9128fccfa473dd4fa8f8496710aa112f31617661961f5e97fd2c3fea061bbfb54946e7687ec43dec8861421bc140c7c1bdb0a3d9b5909cab572d3434a2431c3236a57baefb073fd1bc6d6d1912532702a84570676a2eb0d0047ac4b2bc9fbe5c927e3dceb3c5c249d3cadcbe929008bdca758870e554bc839114d0ba80cca1fca48d28166f949e52c54e6928b1b9605ffb5f5f6b3db23931f7e59b994d47f7c474a3564aeb179664ee01f91770839111d61a992bd1ce5fe02631dddf3d4ad212e79a3aa20c635f97ea8d6db1a1bcf6ec685335f5a88413279438d62e691fc5848efb92ad7dbf44ea4e6f23a8d20414139bc656a727e0ebe69c8a9dc3802d314028e5a8643622b705ae5ae82e01e2578c9d36a252ed8138a7aacac14849121aaba10a66d6ddde63431e9bae2151950ccdf9487c6874ba926706e38df897c4ca3183893c211c3846182cd2588da2d398a58df3e979a559c498fe06a4a7a0163ee286354b8da07da8b9eef9945c618800d7ddab549c895cc7e2ff3000f1781554884bfeecb44e4124d603ebc19b829730a60168980a8fa287fc8f86c0a8a90ab8f38e71f804bba01d8365d1255930ccb79be29beba86407961ab765e979b3dafc32bb1f393d16702fbc3566aedda089832173ca5a7e5069fad1e0660959d49e0bfefd74d2314b393f2fcccaf682a22bea10fe8179d3ba752e8044853c47f4683b4310914f61e58b7a1df42504a74113e6facddec9a9ae5b220a1d6cf4fc0415e9f725e11104af98a7018d5dbf4b5132e8a07a7b4803847e32293d13af6a33d297cf15e40db4bade0a4774ac86b0b12fbacf70560a28f3f7b4db8fa73364a46bb2eb57964005bd8e58c2e61eefb861e701eadc30caea24196fdfaff46b16dc10ecaea90bfdcda36bd80ec0f006d9d2e1bcc89a79ee552a9d1d32fa60bc21ea126a76871ff91f479eebbf887884f360274da76723b26de1d7a5e8ce9ee53c7bfe8b5f29ec186a407e75ca0e3d661862b867f5fe4b79871eed93603d868ecbefd6a9b1188007d91ab5aa9944fde3f6b18da6194ed89cde701078d634b6c54192dd528fa637977e5c3675f4ada2b502e13be091100049adcf52752f514bf95c3fe99e4e20abd509446b5fc66a8857e1d2001066787b655dfe8931150d5648b382f60bbe72d6e66c73e3e6529d54dcf4811cdcc0314c615042bc66bf5173485c9881e9544493687da1731b6293a9679fb04b9622e9b578c81c7608e01f3053cef3f632d4ef5a58f172e8bf12259f6d5f21e25a74e2fa134a379f6308d57c51335552484fb0e88867c9d243bc181a090cb031f58e0fa9f655929adfa77a05baa4894c7de6a6d37290d3eb16283ff637dbd2fdb3500df9f2418aa9a9f250fde66f091fb6ef5acdb61a27902e22fa6d5fdf0057d28171834d980f73ca8ccbc0b303a5062a068e03e7ecfea78e72f78bf920d00be36994a8ee4f42a36ed3a223fba9826a967f946e75760a85a595015b8af2e24ad7c59e99b87f60a5fc7c76e351614576c43732e32d314ddbb85daa881eeed1b150a822d9945663292ce60728bbac00dc5ba3c649e814005a52df5c0fc59b81011cfe6bed95a7ff1808bf7c6e1984dcfba5975ca1768a00381f2974a32d1151e75c2894ff4581c65de16e354ddf938c056c93a50c61579815a9f0282488c76145951a635f929a71f8e662224c9364e12748d715cbc7bc93c5606f07687844222416ad582df9b9091bb790236a28d090291602b43b80c146036ae1d2d3c911123f37a291933fab7e30deea52324ab9ab3adb486903c9960992b5ad8cf6d89670e608c9b4061890a793116ccb7fbce38857e020824d1db42f14fc651164d22a60aaa8f2ad0f20b63778277b1f641d7fc5d12dae47ddfcb2d55e6b870d8b2814c20ffc73af5fadee89dc3b3cd116e881d5c768520e9fca681290ab5c58ead371022b65ed2116f353e440fdbe5d2fdf3f025e2a6d2e9d3e94c9293ceca8086b0de285a7305750eb30cacef7b5469a0797e42080970c0c3e70f990892b6ebff1044023dd8d11b77a419b3aa19d4bdbfabaac8d553dc042fe6c4555b6a14fd64ec980146ddd60ce32d76980b01b71eedf0bdee8b18f198cc0255d73108f17b509bc13025bafa936bc61579f8c1a9930017d1f0b042c5bb0566743b6c731e05e3bd7d1cef12b035210c3ecf81d2caaa99da32fea59317e383b970e419f861ae2573fd2b3c6b4f0c1ca9dcd2203e4b1be938e7838f970bbbfe78707468e381083605301c190b6c5f06802b1dc8b2280acaab86ac5046231ef1060033cc35ee76fe2baf2d661ab0c19ad8987ab3cbb89c97f3c6368ff099e09d1febe8e7f6ecc1479f972b5c27cc320c875b5f129f3b6ff24b4c8a2eb82af590659ba225f3e190fa4e6b1008897acc6e797f4357055c26eeec55360f23d1da9543b6ca7bb34519652626273e877efaf30df25e18578770476876265c499564d49f34e9f6a51e30b1dabe0c32c7acc142ddde363e9e60696c0fab4def3165b7cf5799816352fd5147cbbd053810b943836eae47e715380acaafecdaa5c56f78ad25cd6beb48b30b71e97224546462e575474e305a384c11241bfb4922692f7ada05ae8ffe3eb5a61f4a432346ea3071cda58fee452fbc4db70cd573ef7d17106659050ca1418bbd2681da0af56fd4cc9e2c6418372609222d7387c272a336e2b34a001899e647750744331b117ae4529b0938e4e4712dc5686b6caa250480220d3e94a35b9632e3a73fcd126572f8c3d4ca698b890cd2cec074e5e7a8a7f6f624f385cd36b9d21144013e1d4d49e7dadd46d3bb09ef8d4358a93c905259d943a18b6a020e9701b77274ef12ca1ec8b6d0d112d3b1dd49f29c7bc13ee5e7ce00c1e5ecce92a8c5b0b5b464094dae99c3561ef02693568d05c04104963444dbfbe0e6cd804ab19753689d2f1254bc80d6a1dde41cae49945131b625cbd58bdcf37f4cf699f10aaa7267e1d62acaedd0bb43e6003a32c7112ec16756df55e6b945e730898cfcbaddc25621cb0f3ea6045ecd0c18e3c46ceeee924bcd8b5931869ec38afe8b79b6dcec6e649f26fe612ab89de39ad9fb9d355f67b6608ea70697d9f7125cfa4831a71b894a0a0bf5eaba34f2e38a421e11ed8e30af9a02bac691e911dfd2c5cc8bab6fdcb23904e79827c342bca3644d0ff0be3bb71ebeddb24e04066ab1b9fbb82705a3266a1b742992abbea7a4b968075d5348ff4e1068b01de9997679713f13c50b558d69224e93ff28adec2874a0f0131ed5937a9a84f1a1d27815ef6d4412936fb2d9ab2c9f461e5eb9aaf0b937abf2c25c62926c3052eebbc13a311af28dc949296b271595a279367d738f840aab4617ae5d46cf23b6a6f6b932ffc79d71daf27bccb2128f76718008bc6be9dcdb3c0b25b5172b1da3752ebfd6f9ea2c8c609d906b690b01c120df01729bd22d73b94dd8afa3b34ecbe74d60ea6d449c60645f1528622ff8a547dac1025fa5fce863dddc98f0b30fad91b413d57072fafb52119e58ca1516eee808b2a74efc1cb43944b1974e17032d21093d286720396ff7399fb63c1f79cd6a75644f67501af7e6d876bc562174d7fe4eb0434c4cfee35ac6be75ac1820791537c5df12a815ba14d92b0f4fd6d78169fa6451cfff0eac6ffe080c5c2bd37fc059beb2f3793a7cff7025761e181b81ddd54b11acb6032db51a7a8ed713315f3711ea9f0a97830ae8b36494b8543ac9b862745c4215db849ec6a8fc6a1c5ee285c997e54b3c9fa212c4eef18b07de6901258f3ccd0dcf9ae10e0cec6028fc22b8bdd77d9f5f5753eb121e13d18332b7ab5e4e1b8da6ed82e9149a8261093fe0c9ae8dea8f167327c5614e10282a690196332af402f400d795d62b79112c1b39336e2d239a9a003fd684b32c1eabe3d12736033faf849ae7b6d13f44770823917fd825b94292e2cc911cc0bd9f55e29597fc6463429b0e3113ce09bf1223150eff80c462c613876c1afb7506910c9d57bcc4294dbf2154e84ded32aee02db67e7e6d6ef0b310397c49c631a159f637d7b54fbba46993bde91e06229569289dd10aabdca78e37e5e65111201ba9db43a5d736d09002d9bb979ba354884f0ddc0e655817c6eca1bf4e8399e6059ce13bae13442905698486b29df39028c226fa402d682da8022ed12079080cfe05a60c2764aa1d7b8ce819ef89b73474a62a32750b401f56656c401f6fbefb83b3fa391cbd494326e63cc172aac33ea37c2ab915cfa8935f6c1b643657122c82828d974eb995919f27f9045f1bdaed542120f563c340c7534d63bd89254d5aeb0962d72358fddf008461ffb28c7ca3331224a274f4a23db46311983cebb2a0c25c643781c581dd1853a98484e7daa303272696b4c3fc3250899de7b73273f0cb8271959eeba9f47d6f27845142bb5a30241b037f5bf9b9a97dc46a8293e61834ce50f783911f3be4c332cebb2a1254d2d02770490657b1d03830ed3ee8deb26801236387b361c6422b4119a60b45c17936281de0179aec58b237faadaec4581354ac0fe01e42e4aa3e7d9660abff18d87106ec366b781a8e7b6815aceeebca6d1742fa5b320e1bbccd556fcab55f5df0fa25613aa330b14f831113d7b0e58ce43bf10ffbb5922d8037fd6e8895bec323a41dfc55b695ae69ae883e77735dc097ad451c8943fcdb85658d42a356aa45a10d079f8b8125a7b007f583ba170dbace2b0baeb316a85c9992f4cbe7463bf97e420595f7e9889b31842f7bcb87ee9e1659e67146d3eb1738237d580ec4057b010007e11de07d72ef853d011846238a94be1d45b6af5221dbbaff5d7f2b345aac312ee1f0ba76c97dc811aaf550748da69a8e703e30d162f6c4d5e3bfb584709c610de2f1e1324166ce349aae1e7925db177f435d4dea9943c13a1a14583bc4806811ec85e76d455b6ecfe19375719b71801e53989ffd31d68393958d5db8d28f5e23b717a3076b3f25ae79b373b4c2f8b74ea54e68d5b611508ca732b86b8e682d0d2fcf22fb341c462a809a437c34275a2b1e99cd2f306ca0e5c471513ca2636b189a2f7b2fd610eef2f1f564c494ccf64b450345ad58e46520438d478c922304080088b7ac282721cda9cfa89dfda2df6b663c9e071514b42a6e3e3d3f4b8a382289a0b58bdcc4d9e19fa811d6cf64b4a703bd072cd660021d4d515e59e6df6b3046f12a2efae22d170874e2650dcef1a24d6974f77cecf09d6fbfd8b34feeb40652708e4d2e7170b5938e06b95d7239537f7ff192bded21492eb0b817d58b4d165b06c7bf692e0321aa3778a74dcadc77994701581806ecddaadf49f7104e7f0bc5921a00718a37a6af9d199e6e7d94a2bf032e92c0eb564adf2a7fc68f24701231b482e813783b1f35ada11c23433530cc7bb6fb050f5e9ecd90b18268a629098693da7c2767302986f142e06ccea3467126f980182235dd26f84f3b4168402ab0ffa139de9f700d94fded6ef3da03e6137e64131ab66dbefb912545cfd9ec2140d43b2b04494a1cb5de412225c593576471f8421345c6461404437cd50bdc05e52284204951c094ac5183937c52eb1f89d5efc2ddad671cfaee747cbe411027d28a630695a70bd84e6c8f2204baa7816303b598333816bd2e069cdf2d900c25394ac8abc092015c03de91897529f88364c5920c96d79d36adaa49fa4ee79a1acf5eb5cc8c68c2d207e2c00a4be93e9f7f68fbcf3b22641cc46af5b00e468b266253abf616d8a7219eebf4ed0c186804c9aa518e6498a95d27870b09e9a7910d7d2897abb953838358a1c632c883fdf1da7f014a0145b925a4d62389afc2d21d320b72198c16c59816aa7980f35282f8161997479712acfcbf2ae7fc939048221d36c6e5386de18e2e31bc9151bf31caf26bedd9d9a294b1582a0ee1fd54da2b29ef6dbc542e0a402324832ab4a568d02661bee1222aca13a8b8fedf6d1a212c033e3b768116702bbd085431c7df39c570843069c428878590357edc235a0d12653312e04bbf99f98c84e15b59ad2dc98ebfbb620ea25d8800361638225f0f26fa394aef30b30116a967ec0c6a1fd541f82a8091afda6a6d6efeb080c1e0ad26577e4b645ddd36cb9658dae88d305d1fdc55f0015d1b457fa072d4873e8fdb1e81db21d53d8c4cd8c3d14ce58e0a65f6e4f36053c3c0c3243d19046bd8adb80790a527dbc5ede9d8ecdf60de807d0e924b52c138cd1ac03a04d8e88d07c6de29ec091c14d2da49d5d8782121da331473e86bf1bfe45605de793e288bb1aa71066aab84a29256bd7b881523d47ecc983a2aa4255097c5898bb5af93edfa26d5015bb0c6cd798907d7713346c22b48aee272d38248ae714ea7ec5b82561ad2311fd201458f45a4dd86e7a0f108886af9c8d7613617ef0163f18e25aa9ecb792929c4b0889000b9a49cbc9fb16aa12d3cfc54184f17e50144038d3a2a3626f31e2d9fbc165df3f9fcd75d9e99f535022ec91b19bf7c24a033acc8944dbe79ccf224ce84f0ab04273fd6974069d53931a3a69ba7714be5da0f234df46855e054e8299b8a573817dda6b09843956df7a0e2e21d4ff9d91853431e11061482a315f001d434871e69df4e1f24275172d544c130a1c6d40b3bd6b1d7a3d4520217577129eb8a792342940a31d20af3a99f0f4e005da9e561e5237598bed6ee3ee3820a170ba23dcdec74b1b3b461944639305c1596d3b64c83582a758a345624f51be201d7688c69fddc0ff0eceebb2de6d06dfd6cbb7047857b1ff42f680eccd1ac212b73a863059add2efe748ac8971207921c1195f099f9ab6eb43dd55a303e7cf52525392301fc899dfbe7408424a67c509243e5eccd4cfeeeb00b78288506331f06eaa5fd76a09b44f56feaf2d7af4d23b05308b70ccd0c1d5ad8ad1d47b6ed200fc31fa90f5a3102d27b3b1245948561a7259fef448d208a4d87472bb58b647024fa6e040e10f43ebfa76dff924cd8eb7b20dd80bd5e8cf3253fc75ba2921ba21a0ec8828f4e0b8b951041f4e6b816ba41c45a187c8a6434614b24a67aad33d81226a98970af6c1c254981f8fd1d3325244b579ba13038c2e15e670049d7add31e5883b5055d27d3764f707a699a7c34dedbdc2dfa2388b865e81f2f476a9d74033cf6829ede66868d1df503a99b0ab65dcc9ff4581d674e3cc0edd62f6144551a124eabc55b48029f4d416c598c41215d2e3517fc86598aee3d3876d356fea301e68d91f485409a6939dd9136e8ec513ec47d57cfb3f3e0c130160725b15d712c4d57fb83fce70a48b1f42a06f7652f13a403c42e096aeffd06aa22ababfa6f16f60d110b6e2b66ea8828bff298724e5b5fcf493b2106050b85f98ed06491ac17b4c24d508d47a33aeff7a156692b0f923db58fae1b5cc154be223e28cf7085008215ea995ef6c42fc4c9fbd66a6bcb70b2b38709054ea35cd475bb49de59e10956147508ba2e4fb0382e90998b369f03ec4b14d8d41369fbac5544b4139cbc2bd1cb184c504f95bd229f7df496b1e8148eab68ff7a47356fcb4370c00f9a452fad6ce685e37ef2fbe60c09063c8269b6f4a4f58501bcdd6516d59ff13bc307194265fc4a499426ae6b3d0dbbd38289a438005d3a1230768fe7f22a6591d73c1897db709e4dddee2ffea8f9cf5f7505c38eee0e056bb84b077f668fa3fe29441212919a9819f2e3e22e177096168d5e245ef96e5c116cf5c79748d55620b9901ef44b6da68ac3effcce28cf482c7117c35f4bef183e4bbbc96512a7fdbfe0381833ea263ebec528a88e53d624614b2be689b4755ffb1d27f661b4f5a41301b90dbebf58ff3eb42dbd35814ea7a002d1e718e722d9adad207374586887a5224a2e1c83282cc07d6cd0aa8d1c5758ba79462ddc323bc3bbb930373ef1c53b92ed2d96d4d238155532d1953410af79f3d08aa233e2fb256778a04dbdd925e7bdbc182a8858a15071189ed105aa6a222a7a4ffccd9aed35c4d7f7ba647273e2393025bcc927bc2fd289d1e434e8d15d038ecef09f44680b726df17fe0aab28e386d24fa7843e6fa06f472c82dfaca32d85fd582afd60ad817bfb11b3cae843c751e02776d66f8bf51a55acfa283bc6442d185512af1be7154b7a2da03abbd4a60c4127af50b915ec98859d5c684edf56b08cfe66418f2a5889f9b3a87aef425f6950fe98c14c6fe622d1d1c2bc47cfea2b721e97c8dbb1d9cb37577cd019545b71f0285121e51f77094251805dbd8d085ecfe8ccea6723dc062bb3fedf7c50593b0205922a540423bc8c3d5e42569bb2e7247ea76f0336bcbccbd1186dc9ce7482d03da45465c43bd0de56532970355c7ef777b177c7fe6a7532f317a18f136fde2224e443ab209e43f772d8074fdfea3b067c5381c65bb75025ee3f9dee77ce4dc03c743c2c27d44ef52d099e299389080c52600286711e9e1c734dcdebff96c9219ba0fc80d6ea186e6c29dc5c3191ebcddb75f9a6fa142abb71b0eb24ea3fb870d74ad181209cf52fb62d6a0b7c45d9f539812c01d85dee2e3de2131e9fc0e9e2df8e785877d96f7453353cd8e66536472753c38f34c924a11f9bcd3e1df5b24225211c57d2be13d95cd10a32f573bd85de9b70bda007117fd932d6670a44a0206cebe7808618526d6bc0230034e66ce9f7e8f7696a54be414b4caa9cd0855400c5b7787ab19599dffb8d491cab2af3858179c4829812ccfa8950148db8b0925224e70668ec89f91317e477701835166eeac74a894791d8eb76b1baaa550f11216c0fd4163f586a4e16ea766eeeda37ace01ec98c628056398c52e154fb4fa782f2af6aceeebd78d7f15b25ad97c9903deecb86333fab6be68bd8c9ff9facfe4214f0826fa40d3f55a50d102bcdcfa14680605bf36c2d142623c49239b8c22c31977245235b6d40d8c19937dd89007a42e79bc006ca2dbd5457f812fa1f32d9f1029f5a1a4d325d1518f606530e4b5d112c7fe4fd991d78467c24a0487c346a3258eb48dbc8290f47244f0dfd8152f2c2b6c12d1825bd4ef6c35da4a958ad6db192114ee30ab3d73821882eb27c746bd064c8d29a5f970c77faa3b11299f4bde099787744f327baf3422124943d9662f27c114be514e8c81cf618c21a009e2f82331c0ce3f8705e19f585abb4fd7adf1b9718854ad54a8bff4281e2dbeea99271874da9862988c3799ddd9031aaf478420767989fb32ad7577fa16523d27518e0a961ab41d2f1a80097e95d8c03919749c46f05967906ae8cc3ea5de2d4f25d8110ada472dc029c62964550f9ce1372c049f582e33fad78b81872c25d5e7c0521a3f7c7a4873e16b9def6fc5cb6b8bd7d918e64498de20f4de19f6d0c344e526175ab4d28c8c1f400c5b7b8cc871003b1d5cdcd40bb673a700a1edf8281ebc4a0e3721c6515c27adbc7da9925914ebab3eb9dae0cf2200cfbde8e446c38d7bb4295bcad28c370cbdd42eaac458f3ebe5cb7fa2ef98f984babcbfdf06f34b8f65091ee205e13701a1c2ea8a843f5c4b6efd809fcfabead21b10bde2d146aee526215a4fe1159a4d459d3e0016eaf546adab9cdf32bc4379606278bade85a725f7158a13d111c9446a19f6b0bce14bf1651b10bc0123a2a09faaf6919b112dddaa0a0ea3a4ecce723e76ff8831e4cd13d7424c4be81605104116740d58fb086a712e326db739292746d1985c1f9ae8899358e00d533a75efb5d7de1ad2742836322d49011ef90a17d991fcea02ac3274e6b2a9d8ac45e7227708a6ff1b1434586a11db76e8211ddca3020b2850fb31e09acc20b25a225d945840db8f189168aaed18b1ffa314189d1a899b0bde3931abf4178309c5e553cd375db69e22ba0131d8234633d63ff52a14b8ee82edebff56b20612aaa1828f1836d47cd48a889358a31c2ed3975a00706170f68e5c4fcf468d90ef43d57ea3f28b737ead30045fc1be2f085a00fca043219f011f86030f7f33ac46e1f9796a788f6b1c600c0cd7f74d21781ea6582c1001799f2d348f46f6a81f723560d7e920c1c8fcbda35d6dd4560fbcc1e3fe95963b4b591935b7506b55e309523f6ef0372aa53f6f6f39a8fced3033a3b0ff7bf32b9347d397c052d1114065d98e7a107ca39c1ebb71d52d79f101b2e9d8bfa85029b731263a28b39c9f0e9400d3dc6dc1c9328a022a74b704f2000bf3cb1c7232b14fed08b9c620894f038b79fa0dcd99081b21527561ef27b71531fbc148df29328c03a958ae4a00e803eac930ffcffab30209fff34b7d3f79102a1b45d43dee8dcff85bda9102cb809b22e2e10f32f6af247f3cd2b5db032c29d5334d06da44474e388dba32fef58f6f41b64ac8c0ec6cfcd4dc3e0f2673f92e5b3ff63c00f9d5296e18b42d1abd074dac590e7c6e16d339e714a8168fe9dc14582423af2f47b9078fea487c501377206684605bcb5724e6dc63c1e7822abb0855c33f180b5b20ebb2b40c27267d315c4c2bc75dd5cf4a2a5bceadea37ed78153883fb91a526e6f2a716b7139fe0b86b2a8900372f0172f3aaf9ee4a2019ff06e9f15dcfbba8dbb9dd9f3a28e9f54bfd43de7e0f8482a383ed5b608df6cbb6107cbdb223a442c91a27b79c4bebfab136990c609a006f94a4b4aa357bf3b4d70581cb1e022e7beb4fec5b5fb12d0c964ae273eb8ab4dbc0ca9e38768d621b831eb161ad789abe47c91993aafa4250b09fbac7fcb9f489d93a0be0cc4980ac561a170026532a309839237bfa1081eae9a778f6b16665de3237f00c8124fa1fd3253636290e6dbe6eeae74358d67aec6b012104749f53bd345b362b75ab3b549d088599bea97a0c7955ee2b01338bda7d05c5579e6822b583147e1d0958e70eee1b1d6559c7b4a21947d81d6d7f82d511133b9ce051588ce4a1a3e381ab5775ad0a93dbe6e14e6f4d040a4aacb7864a1468b61fde0d2aad5d57f75c6f9679afe9", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed00000000000000000000000000000000d50b7cdf578f4412a219947e71659c0f00000000000000000000000000000000585506e2fc08284102cd3ce553aa726a29012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002240e1f1fe5145cc75fe7b253dd853cded77fb484b8be9d52aaf0a49098afcc7d24e0b65d370477cc168e835d8143177b582294e4a47357f372f960754b56b54f17b852c2d0067302ffa20e6f7068292fa3304e6e20e60a0c51ed55a2e0c2a2a615de98a2794725029dd283936e25dbbb2d8cf00dc4401eac6e834b094e5efd8e0000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - } -} diff --git a/circuits/benchmarks/results_secure/report.md b/circuits/benchmarks/results_secure/report.md deleted file mode 100644 index c12e1bd71..000000000 --- a/circuits/benchmarks/results_secure/report.md +++ /dev/null @@ -1,141 +0,0 @@ -# Enclave ZK Circuit Benchmarks - -**Generated:** 2026-05-20 09:19:34 UTC - -**Git Branch:** `feat/1525` -**Git Commit:** `e0d477e9e98185779250f4092947741e84169b02` - -**Committee Size:** `H=3`, `N=3`, `T=1` - ---- - -## Protocol Summary - -### Circuit Benchmarks - -| Circuit | Constraints | Prove time (s) | Verify time (ms) | Proof size (KB) | -| -------------------- | ----------- | -------------- | ---------------- | --------------- | -| C0 | 287764 | 1.47 | 26.17 | 15.88 | -| C1 | 2432074 | 9.49 | 26.53 | 15.88 | -| C2a | 3879330 | 10.88 | 25.37 | 15.88 | -| C2b | 5739750 | 19.44 | 25.98 | 15.88 | -| C3a | 3764144 | 11.33 | 26.23 | 15.88 | -| C3b | 3764144 | 11.33 | 26.23 | 15.88 | -| C4a | 2564001 | 9.30 | 26.50 | 15.88 | -| C4b | 2564001 | 9.30 | 26.50 | 15.88 | -| C5 | 4395328 | 17.56 | 26.87 | 15.88 | -| user_data_encryption | 1678200 | 5.98 | 25.90 | 15.88 | -| C6 | 3001847 | 10.47 | 27.66 | 15.88 | -| C7 | 128310 | 0.54 | 26.29 | 15.88 | - -### Artifacts - -| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | -| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042688 | 176160 | 3218848 | -| Π_user | 15.88 KB | 0.12 KB | 2972893 | 193336 | 3166229 | -| Π_dec | 10.69 KB | 3.47 KB | 3553795 | 187260 | 3741055 | - -### Role / Phase / Activity - -| Role | Phase | Activity | Prove time | Proof size | Bandwidth | -| --------------- | ----- | -------------------------------- | ---------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation | 5158.13 s | 127.00 KB | 128.56 KB | -| Aggregator | P2 | combine folds + C5 | 17.56 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | 11.40 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | 10.47 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | per computation output (C7+fold) | 835.14 s | 10.69 KB | 14.16 KB | - -## Integration test (`test_trbfv_actor`) - -### End-to-end phase timings (wall clock) - -| Phase | Duration (s) | -| ------------------------------------------- | ------------ | -| Starting trbfv actor test | 0.00 | -| Setup completed | 3.27 | -| Committee Setup Completed | 20.28 | -| Committee Finalization Complete | 0.01 | -| ThresholdShares -> PublicKeyAggregated | 5158.13 | -| E3Request -> PublicKeyAggregated | 5165.21 | -| Application CT Gen | 7.71 | -| Running FHE Application | 0.07 | -| Ciphertext published -> PlaintextAggregated | 835.14 | -| Entire Test | 6031.70 | - -### Thread pool (same process as integration test) - -| Setting | Value | -| ---------------------------- | ----- | -| Rayon threads | 13 | -| Max simultaneous Rayon tasks | 1 | -| Cores available | 14 | - -### CPU-bound operation timings (tracked in-process) - -| Name | Avg (s) | Runs | Total (s) | -| ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.61 | 3 | 1.84 | -| CalculateDecryptionShare | 2.12 | 3 | 6.36 | -| CalculateThresholdDecryption | 1.96 | 1 | 1.96 | -| GenEsiSss | 0.76 | 3 | 2.27 | -| GenPkShareAndSkSss | 1.24 | 3 | 3.73 | -| ZkDecryptedSharesAggregation | 18.98 | 1 | 18.98 | -| ZkDecryptionAggregation | 48.34 | 1 | 48.34 | -| ZkDkgAggregation | 20.01 | 1 | 20.01 | -| ZkDkgShareDecryption | 30.28 | 6 | 181.67 | -| ZkNodeDkgFold | 78.31 | 3 | 234.93 | -| ZkPkAggregation | 49.05 | 1 | 49.05 | -| ZkPkBfv | 3.85 | 3 | 11.55 | -| ZkPkGeneration | 66.06 | 3 | 198.17 | -| ZkShareComputation | 52.53 | 6 | 315.20 | -| ZkShareEncryption | 114.61 | 36 | 4125.90 | -| ZkThresholdShareDecryption | 251.23 | 3 | 753.69 | -| ZkVerifyShareDecryptionProofs | 0.09 | 3 | 0.28 | -| ZkVerifyShareProofs | 0.26 | 5 | 1.32 | - -Sum of tracked operation wall time: **5975.27 s** (often much larger than end-to-end wall clock -because work runs in parallel). - -## Raw circuit benchmark JSON (Nargo) - -Source files for the **Circuit Benchmarks** table. Persist this directory with -`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without -re-running the integration test. - -| File | -| ----------------------------------------------------- | -| `config_default.json` | -| `dkg_e_sm_share_computation_default.json` | -| `dkg_pk_default.json` | -| `dkg_share_decryption_default.json` | -| `dkg_share_encryption_default.json` | -| `dkg_sk_share_computation_default.json` | -| `threshold_decrypted_shares_aggregation_default.json` | -| `threshold_pk_aggregation_default.json` | -| `threshold_pk_generation_default.json` | -| `threshold_share_decryption_default.json` | -| `threshold_user_data_encryption_ct0_default.json` | -| `threshold_user_data_encryption_ct1_default.json` | - -## System Information - -### Hardware - -- **CPU:** Apple M4 Pro -- **CPU Cores:** 14 -- **RAM:** 48.00 GB -- **OS:** Darwin -- **Architecture:** arm64 - -### Software - -- **Nargo Version:** nargo version = 1.0.0-beta.16 noirc version = - 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: - 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) -- **Barretenberg Version:** 3.0.0-nightly.20260102 - -## Notes - -- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is - effectively 0. diff --git a/circuits/benchmarks/scripts/benchmark_output_dir.sh b/circuits/benchmarks/scripts/benchmark_output_dir.sh new file mode 100644 index 000000000..fd94af1b8 --- /dev/null +++ b/circuits/benchmarks/scripts/benchmark_output_dir.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Shared output directory naming for benchmark runs. +# Sourced by run_benchmarks.sh and regenerate_report.sh. +# +# Four default layouts (under circuits/benchmarks/): +# results_insecure_agg | results_insecure_no_agg +# results_secure_agg | results_secure_no_agg + +# Args: +benchmark_results_dir_basename() { + local mode="$1" + local proof_agg="${2:-true}" + local base="${BENCHMARK_OUTPUT_DIR_BASE:-results}" + local suffix="agg" + case "$(echo "$proof_agg" | tr '[:upper:]' '[:lower:]')" in + 0|false|no|off) suffix="no_agg" ;; + esac + echo "${base}_${mode}_${suffix}" +} + +# Full path under BENCHMARKS_DIR (set by caller). +benchmark_results_dir_path() { + local benchmarks_dir="$1" + local mode="$2" + local proof_agg="${3:-true}" + echo "${benchmarks_dir}/$(benchmark_results_dir_basename "$mode" "$proof_agg")" +} + +# Legacy: results_ (no agg suffix). Used as fallback when regenerating old runs. +benchmark_results_dir_legacy_basename() { + local mode="$1" + local base="${BENCHMARK_OUTPUT_DIR_BASE:-results}" + echo "${base}_${mode}" +} diff --git a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh index d4a6df7cf..717e85a41 100755 --- a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh +++ b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh @@ -3,6 +3,11 @@ # extract_crisp_verify_gas.sh - Runs CRISP verifier test with gas reporter and emits JSON. # Usage: ./extract_crisp_verify_gas.sh --output [--mode insecure|secure] [--verbose] # [--skip-build] [--force-build] +# +# Integration test env (also set by run_benchmarks.sh): +# BENCHMARK_PROOF_AGGREGATION=true|false — recursive fold + folded Π_DKG/Π_dec (default: true) +# BENCHMARK_MULTITHREAD_JOBS=N — Rayon concurrent ZK jobs (default: 1) +# BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER — EIP-712 verifying contract for fold attestations set -e @@ -139,21 +144,35 @@ echo " [gas] Running CRISP verifier test for Pi_user gas..." CRISP_TEST_EXIT_CODE=${PIPESTATUS[0]} echo " [gas] CRISP test completed (exit=${CRISP_TEST_EXIT_CODE})." require_preset_artifacts -echo " [gas] Running integration test (test_trbfv_actor) for folded proofs + timings..." +BENCHMARK_PROOF_AGGREGATION="${BENCHMARK_PROOF_AGGREGATION:-true}" +echo " [gas] Running integration test (test_trbfv_actor); proof_aggregation=${BENCHMARK_PROOF_AGGREGATION}..." ( cd "$REPO_ROOT" && \ - BENCHMARK_MODE="$MODE" BENCHMARK_FOLDED_OUTPUT="$TMP_JSON_FOLDED" BENCHMARK_SUMMARY_OUTPUT="$TMP_JSON_SUMMARY" cargo test -p e3-tests test_trbfv_actor -- --nocapture + BENCHMARK_MODE="$MODE" \ + BENCHMARK_PROOF_AGGREGATION="$BENCHMARK_PROOF_AGGREGATION" \ + BENCHMARK_FOLDED_OUTPUT="$TMP_JSON_FOLDED" \ + BENCHMARK_SUMMARY_OUTPUT="$TMP_JSON_SUMMARY" \ + cargo test -p e3-tests test_trbfv_actor -- --nocapture ) 2>&1 | tee "$TMP_LOG_FOLDED" FOLDED_TEST_EXIT_CODE=${PIPESTATUS[0]} echo " [gas] Integration export completed (exit=${FOLDED_TEST_EXIT_CODE})." -echo " [gas] Replaying folded artifacts on EVM verifiers for Pi_DKG/Pi_dec gas..." -( - cd "$ENCLAVE_CONTRACTS_DIR" && \ - BENCHMARK_RAW_DIR="$RAW_DIR" BENCHMARK_GAS_OUTPUT="$TMP_JSON_ENCLAVE" BENCHMARK_FOLDED_JSON="$TMP_JSON_FOLDED" \ - pnpm hardhat run scripts/benchmarkGasFromRaw.ts --network hardhat -) 2>&1 | tee "$TMP_LOG_ENCLAVE" -ENCLAVE_TEST_EXIT_CODE=${PIPESTATUS[0]} -echo " [gas] EVM replay completed (exit=${ENCLAVE_TEST_EXIT_CODE})." +ENCLAVE_TEST_EXIT_CODE=0 +if [ "$FOLDED_TEST_EXIT_CODE" -ne 0 ]; then + echo " [gas] Skipping EVM replay: test_trbfv_actor failed (exit=${FOLDED_TEST_EXIT_CODE})." + echo '{}' >"$TMP_JSON_ENCLAVE" +elif [ ! -s "$TMP_JSON_FOLDED" ] || ! jq -e '.dkg_aggregator.proof_hex and .decryption_aggregator.proof_hex' "$TMP_JSON_FOLDED" >/dev/null 2>&1; then + echo " [gas] Skipping EVM replay: folded proof export missing or empty." + echo '{}' >"$TMP_JSON_ENCLAVE" +else + echo " [gas] Replaying folded artifacts on EVM verifiers for Pi_DKG/Pi_dec gas..." + ( + cd "$ENCLAVE_CONTRACTS_DIR" && \ + BENCHMARK_RAW_DIR="$RAW_DIR" BENCHMARK_GAS_OUTPUT="$TMP_JSON_ENCLAVE" BENCHMARK_FOLDED_JSON="$TMP_JSON_FOLDED" \ + pnpm hardhat run scripts/benchmarkGasFromRaw.ts --network hardhat + ) 2>&1 | tee "$TMP_LOG_ENCLAVE" + ENCLAVE_TEST_EXIT_CODE=${PIPESTATUS[0]} + echo " [gas] EVM replay completed (exit=${ENCLAVE_TEST_EXIT_CODE})." +fi set -e parse_marker() { @@ -292,3 +311,10 @@ cat > "$OUTPUT_JSON" </dev/null | head -1 +} + integration_timing_seconds() { local label="$1" local val="" - local f + local f blob + if [ -n "${3:-}" ]; then + val=$(integration_phase_seconds "$label" "$3") + [ -n "$val" ] && [ "$val" != "null" ] && echo "$val" && return + fi for f in "$INTEGRATION_SUMMARY_FILE" "$GAS_JSON"; do [ -n "$f" ] && [ -f "$f" ] || continue - val=$(jq -r --arg label "$label" '.integration_summary.timings_seconds[]? | select(.label == $label) | .seconds' "$f" 2>/dev/null | head -1) - if [ -n "$val" ] && [ "$val" != "null" ]; then - echo "$val" - return - fi - val=$(jq -r --arg label "$label" '.timings_seconds[]? | select(.label == $label) | .seconds' "$f" 2>/dev/null | head -1) + blob=$(jq -c 'if (.integration_summary != null) then .integration_summary elif has("integration_test") then . else empty end' "$f" 2>/dev/null || true) + [ -z "$blob" ] || [ "$blob" = "null" ] && continue + val=$(integration_phase_seconds "$label" "$blob") if [ -n "$val" ] && [ "$val" != "null" ]; then echo "$val" return @@ -278,10 +294,64 @@ integration_timing_seconds() { echo "" } +emit_audit_warnings() { + local missing=0 + local v + { + echo "## Audit status" + echo "" + } >> "$OUTPUT_FILE" + for art in "Π_DKG" "Π_user" "Π_dec"; do + v=$(verify_gas_for_artifact "$art") + if [ "$v" = "N/A" ]; then + missing=$((missing + 1)) + fi + done + if [ "$missing" -gt 0 ]; then + cat >> "$OUTPUT_FILE" < **Incomplete on-chain verify gas:** $missing of 3 artifact verify-gas values are **N/A**. Re-run \`./run_benchmarks.sh\` and ensure \`extract_crisp_verify_gas.sh\` completes (CRISP test + \`test_trbfv_actor\` + EVM replay). Calldata gas alone is not sufficient for audit sign-off. + +EOF + else + echo "On-chain verify gas: **complete** (CRISP Π_user + Enclave Π_DKG / Π_dec replay)." >> "$OUTPUT_FILE" + echo "" >> "$OUTPUT_FILE" + fi + if [ -z "$INTEGRATION_BLOB" ]; then + cat >> "$OUTPUT_FILE" < **No integration summary:** Role/phase **wall-clock** rows and multithread job tables require \`integration_summary.json\` or embedded \`integration_summary\` in \`crisp_verify_gas.json\`. + +EOF + fi + echo "---" >> "$OUTPUT_FILE" + echo "" >> "$OUTPUT_FILE" +} + +emit_measurement_methodology() { + cat >> "$OUTPUT_FILE" <<'EOF' +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +|-------------|--------|---------|-------------------| +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus (`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, commit, and hardware. + +--- +EOF +} + # Normalized integration summary object: either `results_*/integration_summary.json` or # `crisp_verify_gas.json` → `.integration_summary` (see `BENCHMARK_SUMMARY_OUTPUT` in e3-tests). integration_blob_from_inputs() { - local f blob + local f blob sibling + if [ -z "$INTEGRATION_SUMMARY_FILE" ] && [ -n "$GAS_JSON" ] && [ -f "$GAS_JSON" ]; then + sibling="$(dirname "$GAS_JSON")/integration_summary.json" + if [ -f "$sibling" ]; then + INTEGRATION_SUMMARY_FILE="$sibling" + fi + fi for f in "$INTEGRATION_SUMMARY_FILE" "$GAS_JSON"; do [ -n "$f" ] && [ -f "$f" ] || continue blob=$(jq -c 'if (.integration_summary != null) and (.integration_summary | type == "object") then .integration_summary elif has("integration_test") then . else empty end' "$f" 2>/dev/null || true) @@ -307,15 +377,17 @@ artifact_size_pair_from_gas() { fi fi # Fallback: folded hex from integration summary export (test `BENCHMARK_SUMMARY_OUTPUT`). - if [ -n "$INTEGRATION_SUMMARY_FILE" ] && [ -f "$INTEGRATION_SUMMARY_FILE" ]; then + local blob + blob="$(integration_blob_from_inputs 2>/dev/null || true)" + if [ -n "$blob" ]; then local pfx ph pubh case "$key" in dkg) pfx=".folded_artifacts.dkg_aggregator" ;; dec) pfx=".folded_artifacts.decryption_aggregator" ;; *) echo ""; return ;; esac - ph=$(jq -r "${pfx}.proof_hex // empty" "$INTEGRATION_SUMMARY_FILE" 2>/dev/null || true) - pubh=$(jq -r "${pfx}.public_inputs_hex // empty" "$INTEGRATION_SUMMARY_FILE" 2>/dev/null || true) + ph=$(jq -r "${pfx}.proof_hex // empty" <<<"$blob" 2>/dev/null || true) + pubh=$(jq -r "${pfx}.public_inputs_hex // empty" <<<"$blob" 2>/dev/null || true) if [ -n "$ph" ] && [ "$ph" != "null" ] && [ -n "$pubh" ] && [ "$pubh" != "null" ]; then proof=$(hex_len_bytes "$ph") public=$(hex_len_bytes "$pubh") @@ -356,8 +428,174 @@ PY echo "$h|$n|$t" } +load_system_info_from_raw() { + local first_json + first_json=$(ls "$INPUT_DIR"/*.json 2>/dev/null | head -1) + [ -n "$first_json" ] || return 1 + CPU_MODEL=$(jq -r '.system_info.cpu_model // "unknown"' "$first_json") + CPU_CORES=$(jq -r '.system_info.cpu_cores // "unknown"' "$first_json") + RAM_GB=$(jq -r '.system_info.ram_gb // "unknown"' "$first_json") + SYS_OS=$(jq -r '.system_info.os // "unknown"' "$first_json") + SYS_ARCH=$(jq -r '.system_info.arch // "unknown"' "$first_json") + NARGO_VER=$(jq -r '.system_info.nargo_version // "unknown"' "$first_json") + BB_VER=$(jq -r '.system_info.bb_version // "unknown"' "$first_json") +} + +resolve_run_meta_file() { + if [ -n "$RUN_META_FILE" ] && [ -f "$RUN_META_FILE" ]; then + echo "$RUN_META_FILE" + return 0 + fi + local candidate + for candidate in \ + "$(dirname "$INPUT_DIR")/benchmark_run_meta.json" \ + "$(dirname "$GAS_JSON")/benchmark_run_meta.json"; do + if [ -n "$candidate" ] && [ -f "$candidate" ]; then + echo "$candidate" + return 0 + fi + done + return 1 +} + +emit_run_configuration_section() { + local blob="${1:-}" + local meta_file + meta_file="$(resolve_run_meta_file 2>/dev/null || true)" + + local bench_mode bench_preset bench_preset_name bench_lambda + local proof_agg multithread_jobs rayon_threads cores_avail fold_verifier + local entire_test_sec verbose_flag integration_test_name + + bench_mode="$BENCHMARK_MODE_OVERRIDE" + bench_preset="" + bench_preset_name="" + bench_lambda="" + proof_agg="" + multithread_jobs="" + rayon_threads="" + cores_avail="" + fold_verifier="" + entire_test_sec="" + integration_test_name="test_trbfv_actor" + verbose_flag="" + + if [ -n "$blob" ]; then + bench_mode=$(jq -r '.benchmark_config.mode // empty' <<<"$blob") + bench_preset=$(jq -r '.benchmark_config.bfv_preset_subdir // empty' <<<"$blob") + bench_preset_name=$(jq -r '.benchmark_config.bfv_preset // empty' <<<"$blob") + bench_lambda=$(jq -r '.benchmark_config.lambda // empty' <<<"$blob") + proof_agg=$(jq -r '.benchmark_config.proof_aggregation_enabled // .proof_aggregation_enabled // empty' <<<"$blob") + multithread_jobs=$(jq -r '.benchmark_config.multithread_concurrent_jobs // .multithread.max_simultaneous_rayon_tasks // empty' <<<"$blob") + rayon_threads=$(jq -r '.multithread.rayon_threads // empty' <<<"$blob") + cores_avail=$(jq -r '.multithread.cores_available // empty' <<<"$blob") + fold_verifier=$(jq -r '.dkg_fold_attestation_verifier // empty' <<<"$blob") + entire_test_sec=$(jq -r '.timings_seconds[]? | select(.label == "Entire Test") | .seconds' <<<"$blob" | head -1) + integration_test_name=$(jq -r '.integration_test // "test_trbfv_actor"' <<<"$blob") + fi + + if [ -n "$meta_file" ]; then + [ -z "$bench_mode" ] || [ "$bench_mode" = "null" ] && bench_mode=$(jq -r '.benchmark_mode // empty' "$meta_file") + [ -z "$bench_preset" ] || [ "$bench_preset" = "null" ] && bench_preset=$(jq -r '.bfv_preset_subdir // empty' "$meta_file") + [ -z "$proof_agg" ] || [ "$proof_agg" = "null" ] && proof_agg=$(jq -r '.proof_aggregation // empty' "$meta_file") + [ -z "$multithread_jobs" ] || [ "$multithread_jobs" = "null" ] && multithread_jobs=$(jq -r '.multithread_jobs // empty' "$meta_file") + verbose_flag=$(jq -r '.verbose // empty' "$meta_file") + fi + + [ -z "$bench_mode" ] || [ "$bench_mode" = "null" ] && bench_mode="unknown" + [ -z "$bench_preset" ] || [ "$bench_preset" = "null" ] && bench_preset="(see circuit artifacts)" + [ -z "$proof_agg" ] || [ "$proof_agg" = "null" ] && proof_agg="unknown" + [ -z "$multithread_jobs" ] || [ "$multithread_jobs" = "null" ] && multithread_jobs="1 (default)" + [ -z "$rayon_threads" ] || [ "$rayon_threads" = "null" ] && rayon_threads="N/A" + [ -z "$cores_avail" ] || [ "$cores_avail" = "null" ] && cores_avail="N/A" + + load_system_info_from_raw 2>/dev/null || true + CPU_MODEL="${CPU_MODEL:-unknown}" + CPU_CORES="${CPU_CORES:-unknown}" + RAM_GB="${RAM_GB:-unknown}" + SYS_OS="${SYS_OS:-unknown}" + SYS_ARCH="${SYS_ARCH:-unknown}" + NARGO_VER="${NARGO_VER:-unknown}" + BB_VER="${BB_VER:-unknown}" + + { + echo "## Run configuration" + echo "" + echo "Settings for this benchmark run (integration test + Nargo circuit benches on the same host)." + echo "" + echo "### Integration test (\`$integration_test_name\`)" + echo "" + echo "| Setting | Value |" + echo "|---------|-------|" + echo "| Benchmark mode | \`$bench_mode\` |" + echo "| BFV preset (artifacts) | \`$bench_preset\` |" + if [ -n "$bench_preset_name" ] && [ "$bench_preset_name" != "null" ]; then + echo "| BFV preset (enum) | \`$bench_preset_name\` |" + fi + if [ -n "$bench_lambda" ] && [ "$bench_lambda" != "null" ]; then + echo "| λ (smudging / error) | $bench_lambda |" + fi + local nodes_spawned network_model testmode_harness + nodes_spawned=$(jq -r '.benchmark_config.nodes_spawned // empty' <<<"$blob") + network_model=$(jq -r '.benchmark_config.network_model // empty' <<<"$blob") + testmode_harness=$(jq -r '.benchmark_config.testmode_harness // empty' <<<"$blob") + if [ -n "$nodes_spawned" ] && [ "$nodes_spawned" != "null" ]; then + echo "| Nodes spawned (builder) | $nodes_spawned |" + fi + if [ -n "$network_model" ] && [ "$network_model" != "null" ]; then + echo "| Network model | \`$network_model\` |" + fi + if [ -n "$testmode_harness" ] && [ "$testmode_harness" != "null" ]; then + echo "| Testmode harness | $testmode_harness |" + fi + echo "| \`proof_aggregation_enabled\` | $proof_agg |" + echo "| \`BENCHMARK_MULTITHREAD_JOBS\` (max concurrent ZK jobs) | $multithread_jobs |" + echo "| Rayon worker threads | $rayon_threads |" + echo "| CPU cores (host) | $cores_avail |" + if [ -n "$fold_verifier" ] && [ "$fold_verifier" != "null" ]; then + echo "| \`dkg_fold_attestation_verifier\` (EIP-712) | \`$fold_verifier\` |" + elif [ "$proof_agg" = "false" ]; then + echo "| \`dkg_fold_attestation_verifier\` | _(disabled — proof aggregation off)_ |" + fi + if [ -n "$entire_test_sec" ] && [ "$entire_test_sec" != "null" ]; then + echo "| Integration wall clock (\`Entire Test\`) | $(format_s "$entire_test_sec") s |" + fi + if [ -n "$verbose_flag" ] && [ "$verbose_flag" != "null" ]; then + echo "| Verbose logging (\`run_benchmarks.sh --verbose\`) | $verbose_flag |" + fi + echo "" + echo "### Hardware & software (Nargo / Barretenberg host)" + echo "" + echo "| | |" + echo "|--|--|" + echo "| **CPU** | $CPU_MODEL |" + echo "| **CPU cores** | $CPU_CORES |" + echo "| **RAM** | ${RAM_GB} GB |" + echo "| **OS** | $SYS_OS |" + echo "| **Architecture** | $SYS_ARCH |" + echo "| **Nargo** | $NARGO_VER |" + echo "| **Barretenberg** | $BB_VER |" + echo "" + echo "---" + echo "" + } >> "$OUTPUT_FILE" +} + +integration_op_total_seconds() { + local pattern="$1" + local blob="${2:-}" + [ -z "$blob" ] && return 1 + jq -r --arg p "$pattern" ' + [.operation_timings[]? + | select(.name | test($p)) + | .total_seconds] + | add // empty + ' <<<"$blob" 2>/dev/null || true +} + TIMESTAMP=$(date -u "+%Y-%m-%d %H:%M:%S UTC") IFS='|' read -r PROTOCOL_H PROTOCOL_N PROTOCOL_T <<< "$(load_protocol_params)" +INTEGRATION_BLOB="$(integration_blob_from_inputs || true)" cat > "$OUTPUT_FILE" < "$OUTPUT_FILE" </dev/null || true) + if [ "$fold_exit" != "" ] && [ "$fold_exit" != "null" ] && [ "$fold_exit" != "0" ]; then + cat >> "$OUTPUT_FILE" < **Warning:** \`test_trbfv_actor\` failed during gas extraction (exit ${fold_exit}). Π_DKG / Π_dec verify gas and phase rows below may reflect **Nargo-only** estimates or stale data. Re-run \`./run_benchmarks.sh\` after a successful integration export. +EOF + fi +fi + +cat >> "$OUTPUT_FILE" < PublicKeyAggregated") +p1_metric="wall_clock" +p2_metric="wall_clock" +p3_metric="isolated_nargo" +p4n_metric="isolated_nargo" +p4a_metric="wall_clock" + +p1_integration=$(integration_timing_seconds "ThresholdShares -> PublicKeyAggregated" "$INTEGRATION_BLOB") if [ -n "$p1_integration" ] && [ "$p1_integration" != "null" ]; then p1t="$(format_s "$p1_integration") s" +else + p1t="N/A" + p1_metric="—" fi -p4a_integration=$(integration_timing_seconds "Ciphertext published -> PlaintextAggregated") + +p2_integration=$(integration_timing_seconds "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)" "$INTEGRATION_BLOB") +if [ -n "$p2_integration" ] && [ "$p2_integration" != "null" ]; then + p2t="$(format_s "$p2_integration") s" +else + p2t="N/A" + p2_metric="—" +fi + +p4a_integration=$(integration_timing_seconds "Ciphertext published -> PlaintextAggregated" "$INTEGRATION_BLOB") if [ -n "$p4a_integration" ] && [ "$p4a_integration" != "null" ]; then p4at="$(format_s "$p4a_integration") s" +else + p4at="N/A" + p4a_metric="—" +fi + +p4_sub_integration=$(integration_timing_seconds "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)" "$INTEGRATION_BLOB") +p4_sub_metric="" +p4_sub_t="" +if [ -n "$p4_sub_integration" ] && [ "$p4_sub_integration" != "null" ]; then + p4_sub_t="$(format_s "$p4_sub_integration") s" + p4_sub_metric="wall_clock" fi # Keep role-phase rows aligned with artifact outputs when folded artifact sizes are available. @@ -447,52 +730,51 @@ cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" +fi +if [ -n "$INTEGRATION_BLOB" ]; then + p2_tracked=$(integration_op_total_seconds "^(ZkDkgAggregation|ZkPkAggregation)" "$INTEGRATION_BLOB") + if [ -n "$p2_tracked" ] && [ "$p2_tracked" != "null" ]; then + echo "" >> "$OUTPUT_FILE" + echo "_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **$(format_s "$p2_tracked") s** — not comparable to P2 wall_clock row above._" >> "$OUTPUT_FILE" + fi +fi -INTEGRATION_BLOB="$(integration_blob_from_inputs || true)" if [ -n "$INTEGRATION_BLOB" ]; then it_name=$(jq -r '.integration_test // "test_trbfv_actor"' <<<"$INTEGRATION_BLOB") { echo "" echo "## Integration test (\`$it_name\`)" echo "" - echo "### End-to-end phase timings (wall clock)" + echo "### End-to-end phase timings (integration test)" echo "" - echo "| Phase | Duration (s) |" - echo "|-------|---------------|" + echo "| Phase | Metric | Duration (s) |" + echo "|-------|--------|---------------|" } >> "$OUTPUT_FILE" - while IFS=$'\t' read -r label sec; do + while IFS=$'\t' read -r label metric sec; do [ -z "$label" ] && continue - echo "| $label | $(format_s "$sec") |" >> "$OUTPUT_FILE" - done < <(jq -r '.timings_seconds[]? | [.label, .seconds] | @tsv' <<<"$INTEGRATION_BLOB") - - if jq -e '.multithread != null' <<<"$INTEGRATION_BLOB" >/dev/null 2>&1; then - rt=$(jq -r '.multithread.rayon_threads' <<<"$INTEGRATION_BLOB") - mx=$(jq -r '.multithread.max_simultaneous_rayon_tasks' <<<"$INTEGRATION_BLOB") - cr=$(jq -r '.multithread.cores_available' <<<"$INTEGRATION_BLOB") - { - echo "" - echo "### Thread pool (same process as integration test)" - echo "" - echo "| Setting | Value |" - echo "|---------|-------|" - echo "| Rayon threads | $rt |" - echo "| Max simultaneous Rayon tasks | $mx |" - echo "| Cores available | $cr |" - } >> "$OUTPUT_FILE" - fi + [ -z "$metric" ] || [ "$metric" = "null" ] && metric="wall_clock" + echo "| $label | \`$metric\` | $(format_s "$sec") |" >> "$OUTPUT_FILE" + done < <(jq -r ' + (.phase_timings // .timings_seconds // [])[] + | if type == "object" then [.label, (.metric // "wall_clock"), .seconds] + else [.label, "wall_clock", .] end + | @tsv + ' <<<"$INTEGRATION_BLOB") if jq -e '(.operation_timings | type == "array") and (.operation_timings | length > 0)' <<<"$INTEGRATION_BLOB" >/dev/null 2>&1; then { echo "" - echo "### CPU-bound operation timings (tracked in-process)" + echo "### Multithread job timings (\`tracked_job_wall\`)" echo "" echo "| Name | Avg (s) | Runs | Total (s) |" echo "|------|---------|------|-----------|" @@ -504,9 +786,101 @@ if [ -n "$INTEGRATION_BLOB" ]; then ott=$(jq -r '.operation_timings_total_seconds // empty' <<<"$INTEGRATION_BLOB") if [ -n "$ott" ] && [ "$ott" != "null" ]; then echo "" >> "$OUTPUT_FILE" - echo "Sum of tracked operation wall time: **$(format_s "$ott") s** (often much larger than end-to-end wall clock because work runs in parallel)." >> "$OUTPUT_FILE" + echo "Sum of tracked job wall time: **$(format_s "$ott") s** — **not** end-to-end latency (jobs run in parallel up to \`BENCHMARK_MULTITHREAD_JOBS\`)." >> "$OUTPUT_FILE" + fi + fold_rows=$( + jq -r ' + .operation_timings[]? + | select(.name | startswith("NodeDkgFold/")) + | [.name, .avg_seconds, .runs, .total_seconds] + | @tsv + ' <<<"$INTEGRATION_BLOB" + ) + if [ -n "$fold_rows" ]; then + { + echo "" + echo "### NodeDkgFold sub-steps (\`tracked_job_wall\`, per fold prove)" + echo "" + echo "| Step | Avg (s) | Runs | Total (s) |" + echo "|------|---------|------|-----------|" + } >> "$OUTPUT_FILE" + while IFS=$'\t' read -r name avgr runs tot; do + [ -z "$name" ] && continue + step="${name#NodeDkgFold/}" + echo "| $step | $(format_s "$avgr") | $runs | $(format_s "$tot") |" >> "$OUTPUT_FILE" + done <<<"$fold_rows" fi fi + + agg_enabled=$(jq -r '.proof_aggregation_enabled // true' <<<"$INTEGRATION_BLOB") + if [ "$agg_enabled" = "false" ]; then + echo "" >> "$OUTPUT_FILE" + echo "_Baseline run: node DKG folds and folded Π_DKG / Π_dec export are disabled. Compare with \`BENCHMARK_PROOF_AGGREGATION=true\` (default)._" >> "$OUTPUT_FILE" + fi + + if jq -e '(.operation_timings | type == "array") and (.operation_timings | length > 0)' <<<"$INTEGRATION_BLOB" >/dev/null 2>&1; then + agg_rows=$( + jq -r ' + .operation_timings[]? + | select( + .name + | test("^(ZkNodeDkgFold|ZkDkgAggregation|ZkDecryptionAggregation|ZkPkAggregation|ZkDecryptedSharesAggregation|ZkNodesFold)$") + ) + | [.name, .avg_seconds, .runs, .total_seconds] + | @tsv + ' <<<"$INTEGRATION_BLOB" + ) + if [ -n "$agg_rows" ]; then + { + echo "" + echo "### Aggregation jobs (\`tracked_job_wall\`)" + echo "" + echo "| Operation | Avg (s) | Runs | Total (s) |" + echo "|-----------|---------|------|-----------|" + } >> "$OUTPUT_FILE" + while IFS=$'\t' read -r name avgr runs tot; do + [ -z "$name" ] && continue + echo "| $name | $(format_s "$avgr") | $runs | $(format_s "$tot") |" >> "$OUTPUT_FILE" + done <<<"$agg_rows" + agg_total=$( + jq -r ' + [.operation_timings[]? + | select( + .name + | test("^(ZkNodeDkgFold|ZkDkgAggregation|ZkDecryptionAggregation|ZkPkAggregation|ZkDecryptedSharesAggregation|ZkNodesFold)$") + ) + | .total_seconds] + | add // 0 + ' <<<"$INTEGRATION_BLOB" + ) + if [ -n "$agg_total" ] && [ "$agg_total" != "null" ]; then + echo "" >> "$OUTPUT_FILE" + echo "Sum of aggregation job tracked time: **$(format_s "$agg_total") s** (parallel CPU work; not P1/P2 wall clock)." >> "$OUTPUT_FILE" + fi + fi + fi + + if jq -e '.folded_artifacts != null' <<<"$INTEGRATION_BLOB" >/dev/null 2>&1; then + { + echo "" + echo "### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas)" + echo "" + echo "| Artifact | Proof (bytes) | Public inputs (bytes) |" + echo "|----------|---------------|------------------------|" + } >> "$OUTPUT_FILE" + for key in dkg_aggregator decryption_aggregator; do + pb=$(jq -r ".folded_artifacts.${key}.proof_hex // empty" <<<"$INTEGRATION_BLOB") + pubb=$(jq -r ".folded_artifacts.${key}.public_inputs_hex // empty" <<<"$INTEGRATION_BLOB") + if [ -n "$pb" ] && [ ${#pb} -gt 2 ]; then + proof_bytes=$(( (${#pb} - 2) / 2 )) + pub_bytes=$(( (${#pubb} - 2) / 2 )) + echo "| $key | $proof_bytes | $pub_bytes |" >> "$OUTPUT_FILE" + fi + done + elif [ "$agg_enabled" = "true" ]; then + echo "" >> "$OUTPUT_FILE" + echo "_No \`folded_artifacts\` in integration summary (export failed or test exited early)._" >> "$OUTPUT_FILE" + fi fi { @@ -529,32 +903,6 @@ else fi shopt -u nullglob -first_json=$(ls "$INPUT_DIR"/*.json 2>/dev/null | head -1) -if [ -n "$first_json" ]; then - cpu_model=$(jq -r '.system_info.cpu_model // "unknown"' "$first_json") - cpu_cores=$(jq -r '.system_info.cpu_cores // "unknown"' "$first_json") - ram_gb=$(jq -r '.system_info.ram_gb // "unknown"' "$first_json") - os=$(jq -r '.system_info.os // "unknown"' "$first_json") - arch=$(jq -r '.system_info.arch // "unknown"' "$first_json") - nargo=$(jq -r '.system_info.nargo_version // "unknown"' "$first_json") - bb=$(jq -r '.system_info.bb_version // "unknown"' "$first_json") - cat >> "$OUTPUT_FILE" <> "$OUTPUT_FILE" </ +if [ ! -d "${OUTPUT_DIR}/raw" ] && [ ! -f "${OUTPUT_DIR}/crisp_verify_gas.json" ]; then + LEGACY="${BENCHMARKS_DIR}/$(benchmark_results_dir_legacy_basename "$MODE")" + if [ -d "${LEGACY}/raw" ] || [ -f "${LEGACY}/crisp_verify_gas.json" ]; then + echo "Note: using legacy output dir ${LEGACY} (rename to $(basename "$OUTPUT_DIR") to match new layout)" + OUTPUT_DIR="$LEGACY" + fi +fi GIT_COMMIT=$(git -C "$REPO_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown") GIT_BRANCH=$(git -C "$REPO_ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") GAS_JSON="${OUTPUT_DIR}/crisp_verify_gas.json" INTEGRATION_JSON="${OUTPUT_DIR}/integration_summary.json" +RUN_META="${OUTPUT_DIR}/benchmark_run_meta.json" GR=( "${SCRIPT_DIR}/generate_report.sh" --input-dir "${OUTPUT_DIR}/raw" --output "${OUTPUT_DIR}/report.md" --git-commit "$GIT_COMMIT" --git-branch "$GIT_BRANCH" --gas-json "$GAS_JSON" + --benchmark-mode "$MODE" ) +if [ -f "$RUN_META" ]; then + GR+=(--run-meta "$RUN_META") +fi if [ -f "$INTEGRATION_JSON" ]; then GR+=(--integration-summary "$INTEGRATION_JSON") fi diff --git a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh index 76e0b9594..3ba027434 100755 --- a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh +++ b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh @@ -6,7 +6,7 @@ # Usage (from repo root): # ./circuits/benchmarks/scripts/replay_folded_verify_gas.sh \ # --summary /tmp/summary_secure.json \ -# --gas-json ./circuits/benchmarks/results_secure/crisp_verify_gas.json \ +# --gas-json ./circuits/benchmarks/results_secure_agg/crisp_verify_gas.json \ # --build secure-8192 # # Use --build when Hardhat reverts with SumcheckFailed (verifier VKs must match the diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 466d87c17..825439a80 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -1,12 +1,16 @@ #!/bin/bash # run_benchmarks.sh - Main orchestration script for benchmarking circuits -# Usage: ./run_benchmarks.sh [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose] +# Usage: ./run_benchmarks.sh [--config ] [--mode insecure|secure] [--circuit ] +# [--skip-compile] [--bench-compile] [--clean] [--verbose] +# [--proof-aggregation on|off] [--multithread-jobs N] set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BENCHMARKS_DIR="$(dirname "$SCRIPT_DIR")" +# shellcheck source=benchmark_output_dir.sh +source "${SCRIPT_DIR}/benchmark_output_dir.sh" CONFIG_FILE="${BENCHMARKS_DIR}/config.json" CLEAN_ARTIFACTS=false MODE_OVERRIDE="" @@ -15,6 +19,8 @@ BENCH_COMPILE=false CIRCUIT_FILTER="" VERBOSE=false PRESET_ARTIFACTS_READY=false +PROOF_AGGREGATION="${BENCHMARK_PROOF_AGGREGATION:-true}" +MULTITHREAD_JOBS="${BENCHMARK_MULTITHREAD_JOBS:-}" # Parse arguments while [[ $# -gt 0 ]]; do @@ -51,14 +57,38 @@ while [[ $# -gt 0 ]]; do VERBOSE=true shift ;; + --proof-aggregation) + PROOF_AGGREGATION="$2" + shift 2 + ;; + --no-proof-aggregation) + PROOF_AGGREGATION=false + shift + ;; + --multithread-jobs) + MULTITHREAD_JOBS="$2" + shift 2 + ;; *) echo "Unknown option: $1" - echo "Usage: $0 [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose]" + echo "Usage: $0 [--config ] [--mode insecure|secure] [--circuit ] [--skip-compile] [--bench-compile] [--clean] [--verbose] [--proof-aggregation on|off] [--no-proof-aggregation] [--multithread-jobs N]" exit 1 ;; esac done +case "$(echo "$PROOF_AGGREGATION" | tr '[:upper:]' '[:lower:]')" in + 0|false|no|off) export BENCHMARK_PROOF_AGGREGATION=false ;; + 1|true|yes|on|"") export BENCHMARK_PROOF_AGGREGATION=true ;; + *) + echo "Error: --proof-aggregation must be on or off (got: $PROOF_AGGREGATION)" + exit 1 + ;; +esac +if [ -n "$MULTITHREAD_JOBS" ]; then + export BENCHMARK_MULTITHREAD_JOBS="$MULTITHREAD_JOBS" +fi + if [ ! -f "$CONFIG_FILE" ]; then echo "Error: Config file not found: $CONFIG_FILE" exit 1 @@ -102,8 +132,9 @@ REPO_ROOT="$(cd "${BENCHMARKS_DIR}/../.." && pwd)" # Circuits live under circuits/bin (bin_dir is relative to benchmarks dir, e.g. ../bin) CIRCUITS_BASE_DIR="$(cd "${BENCHMARKS_DIR}/${BIN_DIR}" && pwd)" -# Create mode-specific output directory -OUTPUT_DIR="${OUTPUT_DIR_BASE}_${MODE}" +# results__agg | results__no_agg (see benchmark_output_dir.sh) +BENCHMARK_OUTPUT_DIR_BASE="$OUTPUT_DIR_BASE" +OUTPUT_DIR="$(benchmark_results_dir_basename "$MODE" "$BENCHMARK_PROOF_AGGREGATION")" mkdir -p "${BENCHMARKS_DIR}/${OUTPUT_DIR}/raw" # For secure mode, patch lib to use secure configs (restored at end) @@ -139,6 +170,10 @@ fi if [ "$VERBOSE" = true ]; then echo " Verbose Logging: Yes" fi +echo " Proof aggregation (integration): $BENCHMARK_PROOF_AGGREGATION" +if [ -n "$BENCHMARK_MULTITHREAD_JOBS" ]; then + echo " Multithread concurrent jobs: $BENCHMARK_MULTITHREAD_JOBS" +fi echo " Git Branch: $GIT_BRANCH" echo " Git Commit: $GIT_COMMIT" echo " Circuits: $(echo $CIRCUITS | wc -w | tr -d ' ')" @@ -269,15 +304,44 @@ else echo "⚠️ Could not extract CRISP verify gas; report will show N/A for verify gas" fi +# Persist CLI flags for report regeneration (see generate_report.sh → Run configuration). +RUN_META_FILE="${BENCHMARKS_DIR}/${OUTPUT_DIR}/benchmark_run_meta.json" +MT_JOBS_JSON="${BENCHMARK_MULTITHREAD_JOBS:-1}" +jq -n \ + --arg mode "$MODE" \ + --arg preset "$([ "$MODE" = "secure" ] && echo "secure-8192" || echo "insecure-512")" \ + --argjson proof_agg "$( [ "$BENCHMARK_PROOF_AGGREGATION" = "false" ] && echo false || echo true )" \ + --argjson multithread_jobs "$MT_JOBS_JSON" \ + --argjson verbose "$([ "$VERBOSE" = true ] && echo true || echo false)" \ + '{ + benchmark_mode: $mode, + bfv_preset_subdir: $preset, + proof_aggregation: $proof_agg, + multithread_jobs: $multithread_jobs, + verbose: $verbose, + nodes_spawned: 20, + committee_size_n: 3, + network_model: "in_process_bus", + testmode_harness: true + }' > "${RUN_META_FILE}" + # Generate markdown report echo "Stage 2/3: Rendering markdown report from benchmarks + gas summary..." REPORT_FILE="${BENCHMARKS_DIR}/${OUTPUT_DIR}/report.md" -"${SCRIPT_DIR}/generate_report.sh" \ - --input-dir "${BENCHMARKS_DIR}/${OUTPUT_DIR}/raw" \ - --output "${REPORT_FILE}" \ - --git-commit "$GIT_COMMIT" \ - --git-branch "$GIT_BRANCH" \ +INTEGRATION_SNAPSHOT="${BENCHMARKS_DIR}/${OUTPUT_DIR}/integration_summary.json" +REPORT_ARGS=( + --input-dir "${BENCHMARKS_DIR}/${OUTPUT_DIR}/raw" + --output "${REPORT_FILE}" + --git-commit "$GIT_COMMIT" + --git-branch "$GIT_BRANCH" --gas-json "${GAS_JSON_FILE}" + --benchmark-mode "$MODE" + --run-meta "${RUN_META_FILE}" +) +if [ -f "${INTEGRATION_SNAPSHOT}" ]; then + REPORT_ARGS+=(--integration-summary "${INTEGRATION_SNAPSHOT}") +fi +"${SCRIPT_DIR}/generate_report.sh" "${REPORT_ARGS[@]}" INTEGRATION_SNAPSHOT="${BENCHMARKS_DIR}/${OUTPUT_DIR}/integration_summary.json" if [ -f "${GAS_JSON_FILE}" ] && jq -e '.integration_summary != null' "${GAS_JSON_FILE}" >/dev/null 2>&1; then @@ -285,7 +349,7 @@ if [ -f "${GAS_JSON_FILE}" ] && jq -e '.integration_summary != null' "${GAS_JSON echo "✓ Wrote integration summary snapshot: ${INTEGRATION_SNAPSHOT}" fi -if [ "${OUTPUT_DIR}" = "results_insecure" ] && [ -f "${INTEGRATION_SNAPSHOT}" ]; then +if [ "${OUTPUT_DIR}" = "results_insecure_agg" ] && [ -f "${INTEGRATION_SNAPSHOT}" ]; then "${SCRIPT_DIR}/sync_bfv_vk_binding_fixture.sh" "${INTEGRATION_SNAPSHOT}" fi diff --git a/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh b/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh index 353a68b3e..f21bfff8f 100755 --- a/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh +++ b/circuits/benchmarks/scripts/sync_bfv_vk_binding_fixture.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash # Sync packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json -# from circuits/benchmarks/results_insecure/integration_summary.json (.folded_artifacts). +# from circuits/benchmarks/results_insecure_agg/integration_summary.json (.folded_artifacts). set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" -INTEGRATION_JSON="${1:-${REPO_ROOT}/circuits/benchmarks/results_insecure/integration_summary.json}" +INTEGRATION_JSON="${1:-${REPO_ROOT}/circuits/benchmarks/results_insecure_agg/integration_summary.json}" FIXTURE="${REPO_ROOT}/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json" if [ ! -f "${INTEGRATION_JSON}" ]; then diff --git a/crates/aggregator/src/publickey_aggregator.rs b/crates/aggregator/src/publickey_aggregator.rs index d3e1da899..5bd76cb39 100644 --- a/crates/aggregator/src/publickey_aggregator.rs +++ b/crates/aggregator/src/publickey_aggregator.rs @@ -362,6 +362,7 @@ impl PublicKeyAggregator { }; let mut dishonest_parties = msg.dishonest_parties.clone(); + let committee_n = submission_order.len(); // Filter out parties that failed C1 ZK verification. Keyed by the real // sortition party_id carried in `submission_order`, not arrival index. @@ -485,6 +486,24 @@ impl PublicKeyAggregator { return Ok(()); } + // C5 (PkAggregation) is compiled for a fixed committee size H; partial committees fail witness encoding. + if honest_keyshares.len() != committee_n { + error!( + "C5 requires all {committee_n} committee parties with valid C1 proofs; only {} honest after verification (dishonest: {:?})", + honest_keyshares.len(), + dishonest_parties + ); + self.bus.publish( + E3Failed { + e3_id: self.e3_id.clone(), + failed_at_stage: E3Stage::CommitteeFinalized, + reason: FailureReason::DKGInvalidShares, + }, + ec, + )?; + return Ok(()); + } + // Synchronous aggregation info!( "Aggregating public key from {} honest shares...", diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index fc2280b0f..7519aa698 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -6,6 +6,7 @@ use crate::{CiphernodeHandle, EventSystem, EvmSystemChainBuilder, ProviderCache, WriteEnabled}; use actix::{Actor, Addr}; +use alloy::primitives::Address; use alloy::signers::local::PrivateKeySigner; use anyhow::Result; use derivative::Derivative; @@ -81,6 +82,8 @@ pub struct CiphernodeBuilder { testmode_signer: Option, threshold_plaintext_agg: bool, zk_backend: Option, + /// Test/benchmark: EIP-712 verifying contract for DKG fold attestations (no RPC required). + dkg_fold_attestation_verifier: Option
, net_config: Option, ignore_address_check: bool, global_shared_store: bool, @@ -149,6 +152,7 @@ impl CiphernodeBuilder { threads: None, testmode_signer: None, threshold_plaintext_agg: false, + dkg_fold_attestation_verifier: None, net_config: None, zk_backend: None, ignore_address_check: false, @@ -213,6 +217,23 @@ impl CiphernodeBuilder { self } + /// Benchmark/test: set fold attestation verifier without configuring EVM chains (no RPC). + pub fn testmode_with_dkg_fold_attestation_verifier(mut self, verifier: Address) -> Self { + self.dkg_fold_attestation_verifier = Some(verifier); + self + } + + fn resolve_dkg_fold_attestation_verifier(&self) -> Result> { + if let Some(addr) = self.dkg_fold_attestation_verifier { + return Ok(Some(addr)); + } + self.chains + .first() + .and_then(|c| c.contracts.dkg_fold_attestation_verifier.as_ref()) + .map(|c| c.address()) + .transpose() + } + /// Log data actor events pub fn with_logging(mut self) -> Self { self.logging = true; @@ -486,12 +507,7 @@ impl CiphernodeBuilder { )); info!("Setting up ZK actors"); - let dkg_fold_verifier_addr = self - .chains - .first() - .and_then(|c| c.contracts.dkg_fold_attestation_verifier.as_ref()) - .map(|c| c.address()) - .transpose()?; + let dkg_fold_verifier_addr = self.resolve_dkg_fold_attestation_verifier()?; setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_addr); } @@ -513,12 +529,7 @@ impl CiphernodeBuilder { .ok_or_else(|| anyhow::anyhow!("ZK backend is required for aggregator"))?; let signer = provider_cache.ensure_signer().await?; info!("Setting up ZK actors for aggregator"); - let dkg_fold_verifier_addr = self - .chains - .first() - .and_then(|c| c.contracts.dkg_fold_attestation_verifier.as_ref()) - .map(|c| c.address()) - .transpose()?; + let dkg_fold_verifier_addr = self.resolve_dkg_fold_attestation_verifier()?; setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_addr); } } diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index cab830ab0..dee0f6630 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -436,6 +436,8 @@ pub struct ThresholdKeyshare { /// the AggregatingDecryptionKey transition. Tuple is `(own_sk_share, own_esi_shares)`. /// Each entry is bincode-encoded `Vec>` of shape `[L][N]`. pending_own_dkg_shares: Option<(SensitiveBytes, Vec)>, + /// Set when C4 verification completes before `PkGenerationProofSigned` is applied. + pending_keyshare_publish: bool, } impl ThresholdKeyshare { @@ -452,8 +454,49 @@ impl ThresholdKeyshare { pending_share_decryption_data: None, pending_c4_verification_shares: None, pending_own_dkg_shares: None, + pending_keyshare_publish: false, } } + + fn store_signed_pk_generation_proof( + &mut self, + ec: &EventContext, + signed: SignedProofPayload, + ) -> Result<()> { + self.state.try_mutate(ec, |mut s| { + match &mut s.state { + KeyshareState::AggregatingDecryptionKey(adk) => { + adk.signed_pk_generation_proof = Some(signed.clone()); + } + KeyshareState::ReadyForDecryption(rfd) => { + rfd.signed_pk_generation_proof = Some(signed.clone()); + } + KeyshareState::Decrypting(d) => { + d.signed_pk_generation_proof = Some(signed.clone()); + } + other => { + warn!( + "PkGenerationProofSigned in {:?} — C1 proof not stored (unexpected state)", + other.variant_name() + ); + } + } + Ok(s) + }) + } + + fn try_finish_deferred_keyshare_publish(&mut self, ec: EventContext) -> Result<()> { + if !self.pending_keyshare_publish { + return Ok(()); + } + let state = self.state.try_get()?; + let current: ReadyForDecryption = state.clone().try_into()?; + if current.signed_pk_generation_proof.is_none() { + return Ok(()); + } + self.pending_keyshare_publish = false; + self.publish_keyshare_created(ec) + } } impl Actor for ThresholdKeyshare { @@ -738,16 +781,8 @@ impl ThresholdKeyshare { msg.party_id, msg.e3_id ); - // Store the signed proof in AggregatingDecryptionKey state - self.state.try_mutate(&ec, |s| { - let current: AggregatingDecryptionKey = s.clone().try_into()?; - s.new_state(KeyshareState::AggregatingDecryptionKey( - AggregatingDecryptionKey { - signed_pk_generation_proof: Some(msg.signed_proof), - ..current - }, - )) - })?; + self.store_signed_pk_generation_proof(&ec, msg.signed_proof)?; + self.try_finish_deferred_keyshare_publish(ec)?; Ok(()) } @@ -2204,6 +2239,15 @@ impl ThresholdKeyshare { let party_id = state.get_party_id(); let current: ReadyForDecryption = state.clone().try_into()?; + if current.signed_pk_generation_proof.is_none() { + warn!( + "Deferring KeyshareCreated for party {} E3 {} — C1 proof not stored yet (PkGenerationProofSigned race)", + party_id, e3_id + ); + self.pending_keyshare_publish = true; + return Ok(()); + } + info!("Publishing Exchange #4 (KeyshareCreated) for E3 {}", e3_id); self.bus.publish( @@ -2732,6 +2776,7 @@ impl Handler for ThresholdKeyshare { self.pending_shares.clear(); self.pending_share_decryption_data = None; self.pending_c4_verification_shares = None; + self.pending_keyshare_publish = false; self.notify_sync(ctx, Die); } } diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 4bcbff437..2e0b528a5 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -71,8 +71,8 @@ use e3_zk_helpers::threshold::pk_aggregation::PkAggregationCircuitData; use e3_zk_helpers::CiphernodesCommittee; use e3_zk_prover::{ prove_decryption_aggregation_jobs, prove_dkg_aggregation, prove_node_dkg_fold, CircuitVariant, - DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, Provable, ZkBackend, ZkError, - ZkProver, + DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, NodeDkgFoldProveResult, + Provable, ZkBackend, ZkError, ZkProver, }; use fhe::bfv::{Ciphertext, Encoding, Plaintext, PublicKey, SecretKey}; use fhe::mbfv::PublicKeyShare; @@ -246,9 +246,10 @@ async fn handle_compute_request_event( let (msg, ctx) = msg.into_components(); let request_snapshot = msg.clone(); + let report_for_worker = report.clone(); let pool_result = pool .spawn(job_name, TaskTimeouts::default(), move || { - handle_compute_request(rng, cipher, zk_prover, msg) + handle_compute_request(rng, cipher, zk_prover, msg, report_for_worker) }) .await; @@ -512,6 +513,7 @@ fn handle_compute_request( cipher: Arc, zk_prover: Option>, request: ComputeRequest, + report: Option>, ) -> (Result, Duration) { let id: u8 = rand::thread_rng().gen(); @@ -519,7 +521,9 @@ fn handle_compute_request( ComputeRequestKind::TrBFV(trbfv_req) => { handle_trbfv_request(rng, cipher, trbfv_req, request, id) } - ComputeRequestKind::Zk(zk_req) => handle_zk_request(cipher, zk_prover, zk_req, request, id), + ComputeRequestKind::Zk(zk_req) => { + handle_zk_request(cipher, zk_prover, zk_req, request, id, report) + } } } @@ -626,6 +630,7 @@ fn handle_zk_request( zk_req: ZkRequest, request: ComputeRequest, id: u8, + report: Option>, ) -> (Result, Duration) { let Some(prover) = zk_prover else { return ( @@ -677,7 +682,7 @@ fn handle_zk_request( }) } ZkRequest::NodeDkgFold(req) => timefunc("zk_node_dkg_fold", id, || { - handle_node_dkg_fold_proof(&prover, req, request.clone()) + handle_node_dkg_fold_proof(&prover, req, request.clone(), report.clone()) }), ZkRequest::DkgAggregation(req) => timefunc("zk_dkg_aggregation", id, || { handle_dkg_aggregation_proof(&prover, req, request.clone()) @@ -692,6 +697,7 @@ fn handle_node_dkg_fold_proof( prover: &ZkProver, req: NodeDkgFoldRequest, request: ComputeRequest, + report: Option>, ) -> Result { let artifacts_dir = req.params_preset.artifacts_dir(); let job_id = zk_bb_work_id(&request); @@ -709,12 +715,23 @@ fn handle_node_dkg_fold_proof( c4b_proof: &req.c4b_proof, party_id: req.party_id, }; - let proof = prove_node_dkg_fold(prover, &input, &job_id, &artifacts_dir).map_err(|e| { + let NodeDkgFoldProveResult { + proof, + step_timings, + } = prove_node_dkg_fold(prover, &input, &job_id, &artifacts_dir).map_err(|e| { ComputeRequestError::new( ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), request.clone(), ) })?; + if let Some(report) = report { + for step in step_timings { + report.do_send(TrackDuration::new( + format!("NodeDkgFold/{}", step.step), + Duration::from_secs_f64(step.seconds), + )); + } + } Ok(ComputeResponse::zk( ZkResponse::NodeDkgFold(NodeDkgFoldResponse { proof }), request.correlation_id, diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 23c4ff5b1..095806b84 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -13,11 +13,12 @@ use e3_ciphernode_builder::{CiphernodeBuilder, EventSystem}; use e3_config::BBPath; use e3_crypto::Cipher; use e3_events::{ - prelude::*, BusHandle, CiphertextOutputPublished, CommitteeFinalized, ComputeRequestKind, - ComputeResponseKind, ConfigurationUpdated, E3Requested, E3id, EffectsEnabled, EnclaveEvent, - EnclaveEventData, EventType, GetEvents, HistoryCollector, OperatorActivationChanged, - OrderedSet, PkAggregationProofPending, PkAggregationProofRequest, PlaintextAggregated, - ProofType, Seed, TakeEvents, TicketBalanceUpdated, VerificationKind, ZkRequest, ZkResponse, + hlc::HlcTimestamp, prelude::*, BusHandle, CiphertextOutputPublished, CommitteeFinalized, + ComputeRequestKind, ComputeResponseKind, ConfigurationUpdated, E3Requested, E3id, + EffectsEnabled, EnclaveEvent, EnclaveEventData, EventType, GetEvents, HistoryCollector, + OperatorActivationChanged, OrderedSet, PkAggregationProofPending, PkAggregationProofRequest, + PlaintextAggregated, ProofType, Seed, TakeEvents, TicketBalanceUpdated, VerificationKind, + ZkRequest, ZkResponse, }; use e3_fhe_params::DEFAULT_BFV_PRESET; use e3_fhe_params::{build_pair_for_preset, create_deterministic_crp_from_default_seed}; @@ -118,6 +119,39 @@ fn select_benchmark_params() -> BenchmarkParams { } } +/// Whether `test_trbfv_actor` runs the full recursive fold + aggregator path (default: on). +/// +/// Set `BENCHMARK_PROOF_AGGREGATION=false` for a baseline run without node folds / folded Π_DKG. +fn benchmark_proof_aggregation_enabled() -> bool { + match std::env::var("BENCHMARK_PROOF_AGGREGATION") + .unwrap_or_else(|_| "true".into()) + .to_ascii_lowercase() + .as_str() + { + "0" | "false" | "no" | "off" => false, + _ => true, + } +} + +/// Rayon multithread pool concurrency for benchmark runs (`BENCHMARK_MULTITHREAD_JOBS`, default 1). +fn benchmark_multithread_concurrent_jobs() -> usize { + std::env::var("BENCHMARK_MULTITHREAD_JOBS") + .ok() + .and_then(|s| s.parse().ok()) + .filter(|&n| n >= 1) + .unwrap_or(1) +} + +/// Fold attestation verifier for benchmarks (no live RPC; used only for EIP-712 signing). +fn benchmark_dkg_fold_attestation_verifier_address() -> Option
{ + if !benchmark_proof_aggregation_enabled() { + return None; + } + let addr = std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER") + .unwrap_or_else(|_| "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0".to_string()); + addr.parse().ok() +} + /// RAII guard that restores the benchmark-specific collector-timeout env vars on scope exit. /// This prevents leaking secure-mode tuning into other tests/processes. struct EnvTimeoutVarsGuard { @@ -767,6 +801,23 @@ fn count_projected_events(projected: &[&str], event_type: &str) -> usize { projected.iter().filter(|seen| **seen == event_type).count() } +/// Wall seconds between first `start_when` and last `end_when` event in `history` (HLC physical time). +fn history_wall_seconds_between( + history: &[EnclaveEvent], + start_when: F1, + end_when: F2, +) -> Option +where + F1: Fn(&EnclaveEventData) -> bool, + F2: Fn(&EnclaveEventData) -> bool, +{ + let start = history.iter().find(|e| start_when(e.get_data()))?; + let end = history.iter().rfind(|e| end_when(e.get_data()))?; + let start_us = HlcTimestamp::wall_time(start.ts()); + let end_us = HlcTimestamp::wall_time(end.ts()); + (end_us >= start_us).then(|| (end_us - start_us) as f64 / 1_000_000.0) +} + fn publickey_aggregator_marker(data: &EnclaveEventData, e3_id: &E3id) -> Option<&'static str> { match data { EnclaveEventData::CommitteeFinalized(data) if data.e3_id == *e3_id => { @@ -930,9 +981,22 @@ async fn setup_score_sortition_environment( Ok(()) } +#[derive(Clone, Copy)] +enum PhaseMetric { + WallClock, +} + +impl PhaseMetric { + fn as_str(self) -> &'static str { + match self { + PhaseMetric::WallClock => "wall_clock", + } + } +} + #[derive(Default)] struct Report { - inner: Vec<(String, Duration)>, + inner: Vec<(String, Duration, PhaseMetric)>, } fn repeat(ch: char, num: usize) -> String { @@ -956,10 +1020,14 @@ fn json_escape(s: &str) -> String { } impl Report { - pub fn push(&mut self, repo: (&str, Duration)) { - let (label, dur) = repo; + pub fn push_wall(&mut self, label: &str, dur: Duration) { self.show(label); - self.inner.push((label.to_owned(), dur)); + self.inner + .push((label.to_owned(), dur, PhaseMetric::WallClock)); + } + + pub fn push(&mut self, repo: (&str, Duration)) { + self.push_wall(repo.0, repo.1); } pub fn show(&self, label: &str) { @@ -974,11 +1042,16 @@ impl Report { } pub fn serialize(&self) -> String { - let max_key_len = self.inner.iter().map(|(k, _)| k.len()).max().unwrap_or(0); + let max_key_len = self + .inner + .iter() + .map(|(k, _, _)| k.len()) + .max() + .unwrap_or(0); self.inner .iter() - .map(|(key, duration)| { + .map(|(key, duration, _)| { format!( "{:width$}: {:.3}s", key, @@ -1067,8 +1140,8 @@ async fn test_trbfv_actor() -> Result<()> { let cipher = Arc::new(Cipher::from_password("I am the music man.").await?); // Actor system setup - // Seems like you cannot send more than one job at a time to rayon - let concurrent_jobs = 1; + let concurrent_jobs = benchmark_multithread_concurrent_jobs(); + let dkg_fold_verifier = benchmark_dkg_fold_attestation_verifier_address(); let max_threadroom = Multithread::get_max_threads_minus(1); let task_pool = Multithread::create_taskpool(max_threadroom, concurrent_jobs); let multithread_report = MultithreadReport::new(max_threadroom, concurrent_jobs).start(); @@ -1083,42 +1156,50 @@ async fn test_trbfv_actor() -> Result<()> { .add_group(1, || async { let addr = rand_eth_addr(&rng); println!("Building collector {}!", addr); - CiphernodeBuilder::new(rng.clone(), cipher.clone()) - .testmode_with_history() - .with_shared_taskpool(&task_pool) - .with_multithread_concurrent_jobs(concurrent_jobs) - .with_shared_multithread_report(&multithread_report) - .with_trbfv() - .with_zkproof(zk_backend.clone()) - .testmode_with_signer(PrivateKeySigner::random()) - .with_pubkey_aggregation() - .with_sortition_score() - .with_threshold_plaintext_aggregation() - .testmode_with_forked_bus(bus.event_bus()) - .testmode_ignore_address_check() - .with_logging() - .build() - .await + { + let mut b = CiphernodeBuilder::new(rng.clone(), cipher.clone()) + .testmode_with_history() + .with_shared_taskpool(&task_pool) + .with_multithread_concurrent_jobs(concurrent_jobs) + .with_shared_multithread_report(&multithread_report) + .with_trbfv() + .with_zkproof(zk_backend.clone()) + .testmode_with_signer(PrivateKeySigner::random()) + .with_pubkey_aggregation() + .with_sortition_score() + .with_threshold_plaintext_aggregation() + .testmode_with_forked_bus(bus.event_bus()) + .testmode_ignore_address_check() + .with_logging(); + if let Some(verifier) = dkg_fold_verifier { + b = b.testmode_with_dkg_fold_attestation_verifier(verifier); + } + b.build().await + } }) .add_group(19, || async { let addr = rand_eth_addr(&rng); println!("Building normal {}", &addr); - CiphernodeBuilder::new(rng.clone(), cipher.clone()) - .testmode_with_history() - .with_shared_taskpool(&task_pool) - .with_multithread_concurrent_jobs(concurrent_jobs) - .with_shared_multithread_report(&multithread_report) - .with_trbfv() - .with_zkproof(zk_backend.clone()) - .testmode_with_signer(PrivateKeySigner::random()) - .with_pubkey_aggregation() - .with_sortition_score() - .with_threshold_plaintext_aggregation() - .testmode_with_forked_bus(bus.event_bus()) - .testmode_ignore_address_check() - .with_logging() - .build() - .await + { + let mut b = CiphernodeBuilder::new(rng.clone(), cipher.clone()) + .testmode_with_history() + .with_shared_taskpool(&task_pool) + .with_multithread_concurrent_jobs(concurrent_jobs) + .with_shared_multithread_report(&multithread_report) + .with_trbfv() + .with_zkproof(zk_backend.clone()) + .testmode_with_signer(PrivateKeySigner::random()) + .with_pubkey_aggregation() + .with_sortition_score() + .with_threshold_plaintext_aggregation() + .testmode_with_forked_bus(bus.event_bus()) + .testmode_ignore_address_check() + .with_logging(); + if let Some(verifier) = dkg_fold_verifier { + b = b.testmode_with_dkg_fold_attestation_verifier(verifier); + } + b.build().await + } }) .simulate_libp2p() .build() @@ -1168,7 +1249,11 @@ async fn test_trbfv_actor() -> Result<()> { // Trigger actor DKG let e3_id = E3id::new("0", 1); - let proof_aggregation_enabled = true; + let proof_aggregation_enabled = benchmark_proof_aggregation_enabled(); + println!( + "Benchmark trbfv: proof_aggregation={proof_aggregation_enabled}, preset={}, multithread_jobs={concurrent_jobs}", + benchmark_params.preset_subdir + ); let e3_requested = E3Requested { e3_id: e3_id.clone(), @@ -1338,10 +1423,26 @@ async fn test_trbfv_actor() -> Result<()> { "Active aggregator: last event must be PublicKeyAggregated" ); - report.push(( + if let Some(secs) = history_wall_seconds_between( + &active_aggregator_history, + |d| { + matches!( + d, + EnclaveEventData::PkAggregationProofPending(data) if data.e3_id == e3_id + ) + }, + |d| matches!(d, EnclaveEventData::PublicKeyAggregated(data) if data.e3_id == e3_id), + ) { + report.push_wall( + "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + Duration::from_secs_f64(secs), + ); + } + + report.push_wall( "ThresholdShares -> PublicKeyAggregated", shares_to_pubkey_agg_timer.elapsed(), - )); + ); report.push(( "E3Request -> PublicKeyAggregated", @@ -1515,10 +1616,26 @@ async fn test_trbfv_actor() -> Result<()> { "PlaintextAggregated must be the last active aggregator completion event" ); - report.push(( + if let Some(secs) = history_wall_seconds_between( + &active_aggregator_history[active_aggregator_pubkey_history_len..], + |d| { + matches!( + d, + EnclaveEventData::AggregationProofPending(data) if data.e3_id == e3_id + ) + }, + |d| matches!(d, EnclaveEventData::PlaintextAggregated(data) if data.e3_id == e3_id), + ) { + report.push_wall( + "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + Duration::from_secs_f64(secs), + ); + } + + report.push_wall( "Ciphertext published -> PlaintextAggregated", publishing_ct_timer.elapsed(), - )); + ); let (plaintext, decryption_aggregator_proofs) = h .iter() @@ -1545,10 +1662,12 @@ async fn test_trbfv_actor() -> Result<()> { ) })?; - assert!( - !decryption_aggregator_proofs.is_empty(), - "DecryptionAggregator proofs should be present in PlaintextAggregated" - ); + if proof_aggregation_enabled { + assert!( + !decryption_aggregator_proofs.is_empty(), + "DecryptionAggregator proofs should be present in PlaintextAggregated when proof_aggregation_enabled" + ); + } if let Ok(path) = std::env::var("BENCHMARK_FOLDED_OUTPUT") { if let (Some(dkg_proof), Some(dec_proof)) = ( @@ -1628,14 +1747,15 @@ async fn test_trbfv_actor() -> Result<()> { .collect::>() .join(",\n"); - let timings_json = report + let phase_timings_json = report .inner .iter() - .map(|(label, duration)| { + .map(|(label, duration, metric)| { format!( - " {{\"label\": \"{}\", \"seconds\": {:.9}}}", + " {{\"label\": \"{}\", \"seconds\": {:.9}, \"metric\": \"{}\"}}", json_escape(label), - duration.as_secs_f64() + duration.as_secs_f64(), + metric.as_str() ) }) .collect::>() @@ -1667,10 +1787,48 @@ async fn test_trbfv_actor() -> Result<()> { String::from(" \"folded_artifacts\": null\n") }; + let dkg_fold_verifier_json = benchmark_dkg_fold_attestation_verifier_address() + .map(|addr| format!(" \"dkg_fold_attestation_verifier\": \"{addr}\",\n")) + .unwrap_or_default(); + + let benchmark_mode = + std::env::var("BENCHMARK_MODE").unwrap_or_else(|_| "insecure".to_string()); + let benchmark_config_json = format!( + concat!( + " \"benchmark_config\": {{\n", + " \"mode\": \"{}\",\n", + " \"bfv_preset_subdir\": \"{}\",\n", + " \"bfv_preset\": \"{:?}\",\n", + " \"lambda\": {},\n", + " \"proof_aggregation_enabled\": {},\n", + " \"multithread_concurrent_jobs\": {},\n", + " \"committee_h\": {},\n", + " \"committee_n\": {},\n", + " \"committee_t\": {},\n", + " \"nodes_spawned\": {},\n", + " \"network_model\": \"in_process_bus\",\n", + " \"testmode_harness\": true\n", + " }},\n" + ), + json_escape(&benchmark_mode), + json_escape(benchmark_params.preset_subdir), + benchmark_params.bfv_preset, + benchmark_params.lambda, + proof_aggregation_enabled, + concurrent_jobs, + threshold_n, // micro committee: H == N_PARTIES + threshold_n, + threshold_m, + 20usize, + ); + let summary_json = format!( concat!( "{{\n", " \"integration_test\": \"test_trbfv_actor\",\n", + "{benchmark_config_json}", + " \"proof_aggregation_enabled\": {},\n", + "{dkg_fold_verifier_json}", " \"multithread\": {{\n", " \"rayon_threads\": {},\n", " \"max_simultaneous_rayon_tasks\": {},\n", @@ -1680,19 +1838,23 @@ async fn test_trbfv_actor() -> Result<()> { "{}\n", " ],\n", " \"operation_timings_total_seconds\": {:.9},\n", - " \"timings_seconds\": [\n", + " \"operation_timings_metric\": \"tracked_job_wall\",\n", + " \"phase_timings\": [\n", "{}\n", " ],\n", "{}", "}}\n" ), + proof_aggregation_enabled, mt_report.rayon_threads(), mt_report.max_simultaneous_rayon_tasks(), mt_report.cores_available(), operation_timings_json, mt_report.tracked_total_seconds(), - timings_json, - folded_section + phase_timings_json, + folded_section, + benchmark_config_json = benchmark_config_json, + dkg_fold_verifier_json = dkg_fold_verifier_json, ); fs::write(&path, summary_json)?; println!("Wrote benchmark summary to {path}"); diff --git a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs index 87ef773a2..7c575d558 100644 --- a/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs +++ b/crates/zk-prover/src/circuits/aggregation/node_dkg_fold.rs @@ -20,6 +20,7 @@ use alloy::primitives::Address; use e3_events::{CircuitName, CircuitVariant, Proof}; use e3_fhe_params::BfvPreset; use serde::Serialize; +use std::time::Instant; fn proof_field_strings(proof: &Proof) -> Result, ZkError> { bytes_to_field_strings(proof.data.as_ref()) @@ -140,13 +141,35 @@ pub struct NodeDkgFoldInput<'a> { pub party_id: u64, } +/// Per-step prove wall time inside [`prove_node_dkg_fold`] (for benchmarks / audit reports). +#[derive(Clone, Debug, Serialize)] +pub struct FoldProveStepTiming { + pub step: String, + pub seconds: f64, +} + +/// Output of [`prove_node_dkg_fold`] including sub-step timings. +#[derive(Clone, Debug)] +pub struct NodeDkgFoldProveResult { + pub proof: Proof, + pub step_timings: Vec, +} + +fn push_step(timings: &mut Vec, step: &str, started: Instant) { + timings.push(FoldProveStepTiming { + step: step.to_string(), + seconds: started.elapsed().as_secs_f64(), + }); +} + /// Run C2abFold → C3 folds → C3abFold → C4abFold → NodeFold; returns a [`CircuitName::NodeFold`] proof. pub fn prove_node_dkg_fold( prover: &ZkProver, input: &NodeDkgFoldInput, e3_id: &str, artifacts_dir: &str, -) -> Result { +) -> Result { + let mut step_timings = Vec::with_capacity(6); let c2a_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), CircuitName::SkShareComputation, @@ -166,6 +189,7 @@ pub fn prove_node_dkg_fold( c2a_key_hash: c2a_vk.key_hash.clone(), c2b_key_hash: c2b_vk.key_hash.clone(), }; + let t = Instant::now(); let c2ab_proof = build_and_prove_recursive_bin( prover, CircuitName::C2abFold, @@ -173,7 +197,9 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c2ab"), artifacts_dir, )?; + push_step(&mut step_timings, "c2ab_fold", t); + let t = Instant::now(); let c3a_folded = generate_sequential_c3_fold( prover, input.c3a_inner_proofs, @@ -182,6 +208,9 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c3a"), artifacts_dir, )?; + push_step(&mut step_timings, "c3a_fold", t); + + let t = Instant::now(); let c3b_folded = generate_sequential_c3_fold( prover, input.c3b_inner_proofs, @@ -190,6 +219,7 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c3b"), artifacts_dir, )?; + push_step(&mut step_timings, "c3b_fold", t); let c3_fold_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Default, artifacts_dir), @@ -205,6 +235,7 @@ pub fn prove_node_dkg_fold( c3a_key_hash: c3_fold_vk.key_hash.clone(), c3b_key_hash: c3_fold_vk.key_hash.clone(), }; + let t = Instant::now(); let c3ab_proof = build_and_prove_recursive_bin( prover, CircuitName::C3abFold, @@ -212,6 +243,7 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c3ab"), artifacts_dir, )?; + push_step(&mut step_timings, "c3ab_fold", t); // C4a and C4b are both proofs of the same `DkgShareDecryption` circuit, so they share the // same VK. Load it once and clone into both witness slots. @@ -229,6 +261,7 @@ pub fn prove_node_dkg_fold( c4a_key_hash: c4_vk.key_hash.clone(), c4b_key_hash: c4_vk.key_hash.clone(), }; + let t = Instant::now(); let c4ab_proof = build_and_prove_recursive_bin( prover, CircuitName::C4abFold, @@ -236,6 +269,7 @@ pub fn prove_node_dkg_fold( &format!("{e3_id}-c4ab"), artifacts_dir, )?; + push_step(&mut step_timings, "c4ab_fold", t); let c0_vk = vk::load_vk_artifacts( &prover.circuits_dir(CircuitVariant::Recursive, artifacts_dir), @@ -282,13 +316,20 @@ pub fn prove_node_dkg_fold( c4ab_key_hash: c4ab_fold_vk.key_hash, }; - build_and_prove_recursive_bin( + let t = Instant::now(); + let proof = build_and_prove_recursive_bin( prover, CircuitName::NodeFold, &nf, &format!("{e3_id}-nodefold"), artifacts_dir, - ) + )?; + push_step(&mut step_timings, "node_fold", t); + + Ok(NodeDkgFoldProveResult { + proof, + step_timings, + }) } /// Inputs for [`prove_dkg_aggregation`]. diff --git a/crates/zk-prover/src/lib.rs b/crates/zk-prover/src/lib.rs index 4d8707646..136eedd35 100644 --- a/crates/zk-prover/src/lib.rs +++ b/crates/zk-prover/src/lib.rs @@ -26,7 +26,8 @@ pub use circuits::aggregation::c3_accumulator::generate_sequential_c3_fold; pub use circuits::aggregation::c6_accumulator::generate_sequential_c6_fold; pub use circuits::aggregation::node_dkg_fold::{ prove_decryption_aggregation_jobs, prove_dkg_aggregation, prove_node_dkg_fold, - DecryptionAggregationJob, DkgAggregationInput, NodeDkgFoldInput, + DecryptionAggregationJob, DkgAggregationInput, FoldProveStepTiming, NodeDkgFoldInput, + NodeDkgFoldProveResult, }; pub use circuits::aggregation::nodes_fold_accumulator::generate_sequential_nodes_fold; pub use config::{verify_checksum, BbTarget, CircuitInfo, VersionInfo, ZkConfig}; diff --git a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts index 6043dae8c..a8bbaa6b1 100644 --- a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts @@ -94,11 +94,18 @@ async function main() { let decPublicHex: string | undefined; if (foldedPath && fs.existsSync(foldedPath)) { - const folded = JSON.parse(fs.readFileSync(foldedPath, "utf8")); - dkgProofHex = folded?.dkg_aggregator?.proof_hex; - dkgPublicHex = folded?.dkg_aggregator?.public_inputs_hex; - decProofHex = folded?.decryption_aggregator?.proof_hex; - decPublicHex = folded?.decryption_aggregator?.public_inputs_hex; + const raw = fs.readFileSync(foldedPath, "utf8").trim(); + if (!raw) { + console.warn( + `[benchmarkGasFromRaw] ${foldedPath} is empty — integration test likely failed before exporting folded proofs`, + ); + } else { + const folded = JSON.parse(raw); + dkgProofHex = folded?.dkg_aggregator?.proof_hex; + dkgPublicHex = folded?.dkg_aggregator?.public_inputs_hex; + decProofHex = folded?.decryption_aggregator?.proof_hex; + decPublicHex = folded?.decryption_aggregator?.public_inputs_hex; + } } else { const dkgRaw = findRawJson(rawDir, "threshold_pk_aggregation"); const decRaw = findRawJson( @@ -112,9 +119,24 @@ async function main() { } if (!dkgProofHex || !dkgPublicHex || !decProofHex || !decPublicHex) { - throw new Error( - "Missing proof/public_inputs hex fields in raw benchmark JSON", + const out = { + verify_gas: { dkg: null, user: null, dec: null }, + source: "folded_proof_export_plus_crisp_verify_test", + note: "Missing folded or raw benchmark proofs — run test_trbfv_actor successfully first", + artifact_sizes_bytes: { + dkg: { proof: 0, public_inputs: 0 }, + dec: { proof: 0, public_inputs: 0 }, + }, + calldata_gas: { + dkg: { proof: 0, public_inputs: 0, total: 0 }, + dec: { proof: 0, public_inputs: 0, total: 0 }, + }, + }; + fs.writeFileSync(outputPath, JSON.stringify(out, null, 2) + "\n"); + console.warn( + "[benchmarkGasFromRaw] Wrote placeholder gas JSON (no proofs to replay)", ); + return; } const dkgPublicInputs = hexToBytes32Array(dkgPublicHex); diff --git a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index 50f2c08c3..adab41173 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -36,7 +36,7 @@ const COMMITTED_FOLDED_ARTIFACTS_FIXTURE = path.join( ); const INSECURE_INTEGRATION_SUMMARY = path.join( repoRoot, - "circuits/benchmarks/results_insecure/integration_summary.json", + "circuits/benchmarks/results_insecure_agg/integration_summary.json", ); type FoldedArtifacts = { diff --git a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md index c289ecab3..ee04bc4f7 100644 --- a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/README.md @@ -6,7 +6,7 @@ Golden `dkg_aggregator` / `decryption_aggregator` EVM proofs for ## Automatic refresh After an insecure benchmark run that writes -`circuits/benchmarks/results_insecure/integration_summary.json`, +`circuits/benchmarks/results_insecure_agg/integration_summary.json`, `circuits/benchmarks/scripts/run_benchmarks.sh` calls `sync_bfv_vk_binding_fixture.sh` and updates this directory’s `folded_artifacts.json`. diff --git a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json index b077dcf15..ea446f187 100644 --- a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json @@ -1,10 +1,10 @@ { "dkg_aggregator": { - "proof_hex": "0x000000000000000000000000000000000000000000000003f7062d91e599a1d9000000000000000000000000000000000000000000000008cc7102c928c65c450000000000000000000000000000000000000000000000075ba3dc02d69ce9910000000000000000000000000000000000000000000000000000ed71a67536b000000000000000000000000000000000000000000000000e44e8edaa774d8d700000000000000000000000000000000000000000000000069df7c477ac356a5a00000000000000000000000000000000000000000000000546c35bc9a1e69c5500000000000000000000000000000000000000000000000000021ee487cecdf100000000000000000000000000000000000000000000000ab881c361ac7b250800000000000000000000000000000000000000000000000615484820700c856a00000000000000000000000000000000000000000000000bb5ddeeb0299d72f60000000000000000000000000000000000000000000000000000614a8dc34464000000000000000000000000000000000000000000000008f86b47c227146d86000000000000000000000000000000000000000000000005b0124c6d784e70a800000000000000000000000000000000000000000000000c7ce22f6009a0150300000000000000000000000000000000000000000000000000028c352ad51c1f0a1e934d28e26ddbc2239f70f1f2a218f011eacde911ac55ca4f56e4c92826ba0151ec8baa1515407b51df92e0368977054f4ff1d48781d8c1bc7962286ed7d20c520eaffc4f8e6351751bd8fe1e162471d0fe3163d177d0ead3ac22d2519a17089d86113a4a8393c93b80f3bb7c9dd5bab24c560c9f0d8906322ab4d14bdf2527575e8ae48fb40af0adc9c7df54fcb65138a862afbaf04d592215d462bcd29107c4ee919449aaed15296c3db7bc5d246d1753696a3fb0d71ae9e02085318c402afd41a4633e857acd0967c13bf26c50c6457ab5dd898ac1864799b9730689d52ad93c530511536a3714b5748aa649245ab18e286181818ec79f628930d4b39d1af275a9bbc79f11513fc819f8a77429a99f5c19dbc3aaa699028fd84578ae771d2cb42f9080e97577d5433096dc7b5872c8a57058cf2b02f8c0e531169ac86d010bdbafbdca84bfb0e04bad65fe3350d55fbfd17c3806f415f2fef12c85a5cb05693555777afb1ed8c9e98ab2221191471b57f70880883d7f77cf7774d4fa8726d1e7542a8d79306c76a221eaf68d96dcc25f7bfa67547c7e7e6823a8f5036a0520516bf26d60b94a8dc1de281b183f97c760db8ac7dd87c986a7ab87c57a3521148a502af09952b50e41ce4798bccddf61aefc5c08cbe35204f43d0c36954a0e89229a121eb3d18ac0c9561aed66af56e683b1ba590eaa4edc1d6476c2762208096dced400b6f35fcbf000acf60ad54d9076eec61d80121dfff0afe26e749c2c7dc41f5ec8f3774ba1084aa7c4d494798c31c4b33f0371083a13e67aaaa9fb297e0ddb44886687d5d35b7485cb47105303ab9d1a237f8dd9dcb93298077c2c1bf65619bd87d561ea96e41c5106896c2a1307eae3afc8ce2c501e6ae5183d9b302f94e692484cba7c25419a42dac6badcb139955e149eccdee3c1b2bbb39bd92006d24ae9e0b0607f5c1994f5c0eec60fdd95dcd71c256fde7e12934ae95ff62c1c3afe38e4250234c03b3fbe574ebdc4b6acce335cffc79409260039c2c5890f33e7fc5c1904725888d81fbb00dfe5829b22b86c60cc60282639614406840619a2a11d45659615c74b56c03d94902360a16b8813b318f90cfdf6d941bd27c11c2c40020df0c8df1b5f56cf8faf6e3f78bdd34f134ab0857d93cc7fc0d8d26a084aeb7c91a1d79ae529ded891a7a2a94b774927a6747644bc87afa45c115cef199c133466b246d8a8f6a87f50454203689ffe4a63c722e36cd714d94d5d8ec715203e52c6915f66cbd5ef50437c703b5c3393cc645517d6c2a210f9ac23d88a2d480d46aa3febce868435cb4052c6af52d836aa60a7141cd972100fdea65c6623f51577a4fdabe52375af64863d09b294de12c786066a4510cf18737b5cb2521bbd58b29c859db5be07fa8057c61e27972541464105d7346270eb1eaf8183f00b03e6634f391192f98965c1fe150acaf8decb6acd8ae9578aba334c8ceb816f220f0a141a0b9c666e712d8917ceae41edfa816a4a3cc319116df04818fed2c5199163f3eb27e7de5e4d47b7a22a1e7e7186dc3a4ec5744e6b26dc0ac4fe236317e300bfa4ac8d1c52a22a867753dbec2a0ef3ea56b28a978dc21c6d56e0a9822cab56055b0a192a4f2a1fa9a1e2ad0d98fb85051bad9fcefaff281aeb31781b070f5b929a644a885cc948d2b8e9b7948b588a829b836bd61f9087f753533e5c0e79b5fc84e0de7671e00294fc3a78e2ad1e7d469a0e2cbc379f6494954a946d23e35a296d5c94f4cf03f663f7328be01a3aa375f6adfc6e3e10f89456df78a3240903d00116a9b3572d29437bfede50d55590b4df762150fbfdd6fee2c519302c37cdbd0ca0131da0faef83a2b57f89150a76f67a22fb2825ae844a2c0e55a62c00585cf13f7c290242ac5610faa0fee3018541c5733b68415f6f999951b90921e98735f1adb294593b17e6678722213e8c446ad3a7ac022936b20a6d77b0f6176f505aabdbc699bbc6e19d082fb8318459555069abbba3d01c1b9b552da6c8174ffa28093ab52aa34c5a04e72119ca3dbeb427c31876f8a012fa6be8dbd2a81076baa410047ec186a7ca713a00714cf370d06de1847c65f81e0023042b1f491eeeff34d3b6e787870dbef2665bc02dca4ec8c0fddf496de2e31c7450d626fd10ea411776b22e3ceb7f3891f81080eabfbe32548bb88663fa270ebba72fc8c5243643188bc9ee4d07de409f501cbfa49afec7c87d1f2789d2f932f298ccc33c0fb18246887d583e8a5966746fb3bae0cb7f3053e7d807c47ee679166cfac9f11031de5328aa1e8cbbfe748655b5ac3f7b303a92328c64d738e337e2c88e4b1815aa4e0f6573d470282ce50581e75a13f328a3a0b9a1c263f5180223110576da26d8a27a0e4e674729f076e96b629ad9b2501d8f8d437d8ed00e908692403aa12b370df72df67acd5d66fbd6d6e9f3341101c4fe455c6429cbdc3d843b52ae36215a43ac8698db6dcb1ce12497b9f51d8cd0c4b78b96bd81f24d574177a98f2c19090146ab59f7d5f461a0c6bdad1e75b6da38e9147b88a655ca582581ad2b64274c7a63e1cfd278616f7d185902cc57cd18f89bbb4ce2dd17cecebad3c3ae812450131532f01a9c5c62f6bd9dbd817f5b7a07ed301ed9d92083d0724cf8a2221e263a087933e462f1975e9d2aef17772cb92841fe96566553bf90ad051da81e0bb61346c75d7de2a99a99001e56bd9dbb08dab169ec147bd7da07e01518d6951476e1758afce02df80c3fbdc515f04cb5bac48c201aa8b1fe54a520908d01d71def68a4993955aee64e9ca2d4b85bff5404f51696a7f26ca335b112900df31029b365ff086b60ab3d13c2c44465a1656c82a1472c6471bd0a018542139b4bee2741b75a8797dca4cdea4df105561471e1925e46a05a495ccb95a62bda8f2fa905bf990e5f7a30b9ec101e503b1c44ce94d6d751db5d4d01adb185e7e3de9f27268e33e71fc0136c349931194a638570f3771419d2b2dcddd74580e61cd0e1d82651a4f74f8bcb480b02daa45a841fc3f76890eff255b85a88bc204442cc35b501e65b950d5618397f528e8835d8167c888594c0b3d3d56335e4968440b7579c18a71da291ff2dd20bff984b0809d0a72ffa9aa15e477ab40e998e1845754979039b052e98b768bd8ec0df44278c207269026354eefbf6d32494347009c580731b668298f1615b5fd77cf4d43bf2f6319db631859267780752e728fb52d39eaa1f8794c0aabcd93d68844a49b146200a8dae0284da464d105fb70dff102798180231d29917b244496fab251ab80197d5b680b97fc6560a74d4627bc8702da1430dd7c2e80e72a633dc52613da653e24c43173f0374fb9ee7c31d45109a2f2dda1a6ed6e02c387274f3dd41d3d30ebbe2d0e5be93a288321e54eaafee3ae3408d2a209a285a1286870d7081d8d7987d96c0d96ca7d38d53de6db74497ebe57c97239e24d134e668da1e614fc31b92e571228eeb9eeee087b5e87183899701652d1223a0c17880be1879a41e3e4444a426ec0e9fa4af9628907a42e4a40d9aeb57244d40642f454a684f07fb45974600f06e72ba7ae4dc8ebe05a4346ff12279811e1e8ac8dabd36dc3b58fe17eb6bda441f9d75fae5a431f131ef567c49334a2026b1b06d0d09e4dc77fd44f9bba7003e5f6b6b6a78d19cd2ddea68492d8d5cf918fd982caf0cf885d3a0819ffb8da3d24352207baa1802784bde523e1d4071772552c303070de53ae5a62aae59b858aed5260f66cf269d2fbe57df7c3960c84c00ce310e336aadf9d8ac732b8504f61775296afd57ac458c4b3f587e9095473628876de23bdcea9df2fefd44148b794984b966027bb3835f4deb5d0323e3d8512839d9d7e91bbd6d0b579a8269256fadf864f388ffca58afddd505f1009a887403f654353a693b5449c09424816a4f223999c0261b85fc9e27f8044a685fd97e21c348d3131cd0ad8ab11361ad8bd542e13b95cc94bfc6f4e02864e77bc3a5ed27eadddc4e835359c11b295e862d4647e0be8151f1472a2bad617d662049c258270ba05cc3c8fee60b79dbe3cdd5892d467eb1eb919b10908c1e6bce7cd6256f08b90d498edff9b990d45e8517e68b155bde445476e86361a62bfca66c782d9e1d5020f5aac68add0d8d5e11313bd34806eb1d07998bac8aac78045bf62ef35d1e6c68699ae309a357eb91767cc97f41afc839fc0e2acf149f03f5e4768ffe12239d5ec835b63dedd8ef257ae0ecf932ea82089a9b334f50443d89da8b9d940d17cf45a436d206057e1baac60dd3d4fd5053184b5fb725563e2f5dac366e47a41671e27bfa1af5d7604965af6f65f9cbc0e187826e3034396ad39269a7828deb009d86c1876e90e2bdaf2310456e63048c0ad17f98d7e3405a57c62f0891862c22868eac6f10b58ad7476cedf652fcb48c170c458ea95e0dab1300d1477d371a0a83855b2b4be0f913d533698dc00b61e6e0d98172e11cfecd9f2ec88fe7e1282a8e3523974469c524c395b19cd1b2ae2e6a0f08b8b7e9925dd16241984f02fd028654ca0f201085d96833d6c571a08c2b680b77e6374767fd732a315e12b8c0150e7ce2da2707c402b007853edf703e5a06659abccc5c7d539a6303c10a747a1e1e6c660a3493a44ad142c672094981fe9b0699eb6202ee481875332cde412924e4d4f54c75ba290478813ee7b7f8d2cb73c42bdc9b4db044a2c033356a397d2c4c27dacccc0604af0ab1b240b25d79bd8efba3cc58a7d4e98a56ef93e694d61dd9e5f7b64e66744fba85443990e2ed06eec8d4edba99ed139bc3fc816ff1e925b004f64a5e0fad8b437791220910fb7619b648984bb76620b9995c1e0a69a700c51f7b25bbc228f229520d7655200d19e0e155637260069e8ecaadaf56dfda0b606943398c2acd8244ef5e50f797377dfc6fa9ee06f4275f03912c8b039a4e10545dafb7eb5d56f282fddbe00bfa22c42731fd13dcd08237f88c870a6b02d821c0cc4c3cbaee07da622638faeca1c035134da588eb3a47298ff1f76c5bcb1b07713872aefcf57c09befd5274046db1e94bbf346a9618cec87c5d833b3cb6bd1fb1d5fe9d37ad9b8f7d69d27e74ee6d11442b4072971d6e308c90d2f28f692c154ec89bf6cbdc150ce4340973cbcaf071d3a6b86f43c96c95bb68fe435b719e204f06a549b9859201bfb3c8f674334d15b92441bf420dad6b8ba7ed93a4b10d2446d9f477b8f4ec9d7d61dd89ffaf24851b9e1af79ef09d04fe4c7d3b8aa5fc0da2c0013fa968fc23f85734436cd57b203429df6c86e228b25d897c432a70cb14e82c91259ac6b99a472f04cb05da560a649d84b1d96e532adbd39ce1d118e024519d5d192db13745d58bd16158c16f79f890268fe33acf3640cd7fbd97228a0ad8f596ad7c30f5fd9a30c3a58d741a2d1248c01cf56fb12d646351b1d7bdec09b42a222f01fd7c10403cefcf03c0fea4708557da361d24f964088d362babea2032fff91add268b7e2260474cc0c1aae1ec3526be33d9724af9298ae7e94117125ed9034842758be7da9d9f46146f157bcdf61f8f0ce96b32970cf633a17db02a03183311f34d9192fb47068e905383e1fb9a893dbe99e7852c091eb056d2df2a36fcf2ea043a1f9f80df7d2e61ed2d3f3294a2bd6ce92f31dc6d8bbdab4f6d1c9e678355d1fcf76296effeb331d4e2fe67a71da03da01f1a18cf069553eaaa13980e46467f02c69bea54c3720548b3123a35dc4d419d66c373b9089ce74d2f1c887e07f9068b2b4151380e03ab3782c4d3477698d60306b49d96880133708412e7aa8c948838ad2e7c574a87420d2eaf8a25535161adfaf0c5dc8f246e6f831f0b0d07d5c1786ecabd3ba2c3c693d53d95d3c61376e795acc867eeedc7c22e069b5d13c0155fb52b2f949bacdfedadeebf5db8d0bf6c78a11124a650873d00014105f5c4d5e9a76723d3c67939a223b0e21dd47410856f6109731f3fd4573914528d4d0451b6e9852c06874f6a8f499644f82ff8016134533e19e7543937fa1b79e556eb2a677f43c40ec09e8c2385b5e5dda6215ab070253c5d3e53b2ff7a00a2bc71a7012e60f4ac98fe1840e7be8c09aa648b3ab0f0c27085569d6460b8028f028a0fe35cbc839b5c9e28401bda72c3972c2888d203f378d76751e957740c1177fdc8d98ddc08e28b05c069efa51c8252cbe0cf818abfcd395484981c0e2b80cc558a8a10aab8b962daf92a786433511af9f8527aa2a49c2b8bd53101d105fc145055d9ba564709104c5549bb24a3dd91a505e47959f719258a1cc6dccd06c3b905097f428b5cff87707bd0e1ce71ece41f31ad817ff1f3d04630274e7707bac1c17fe327bb81353d07980eb53d4602c580dda7f7f15e59e57cd7dbd3d90613f1fd884030e3bbfcb0d7efdc94158660e2fa5c7d49d11e08a9b723087892209124075b7d82394c4ac6a6a3e6744b706cf7f538fbe24358c280b1c6d241ec04a10e305eb956910f0d968d0f83280049572e3e7206b91ca83f558ff6a01694227174a5061617717a39664d6b54a851e1ab5c9041c80952a220f62d183fd4ff02f9d499f6808334fb92a96a4a1332be8b96b264d7ace5e3dde89aa066da81471f8dc4f4c40b4ff98e4b85557de6e994baccc86846ab05cd2e6ab2f7affcf99c008ccbcbedd42d97aa344146ad1359db6ef1c3c8e7ee17fbbadcf7d53c7b749b00d57fead071621cb4046c8d9c40b138b1e897b30d7872b654bf975575b18b540e2b702474b75054fd96c52543d85e68751471a807905b532f7b72afa62bc86c08d20514704803dc419c284bc0b953f95b2c5c957979546e79b46c2596fcd7bb07804adccc6532e423df312c056d852c6730935f95c1ff85556161503263da9e10b45be2e0cfbcc72420fec0109fef3fc1d69a1610ae256cda6fa0b1edb22e6325599a344ed9500bb3b6db80b3ed4b856d798aeb51be3bacd83e3ece79a68d750423ee12cd58ed06961b157814caebef95e7bb0384213e8f34c3f364f7e0479b248d3ce8d2005755b5832407fc03cfdfd570903d70ab82f55e01499f0e533dff1d1ce6f0c124b0fa6dd880e2c715a08bd9834659fb5dad9de7e79fed5471144c0caea76eb72fd11995bbcd26b93c3eeef7bf4613270088c9d292c5b16a2b9c5f064ed3eb9f013c40d3b53e08486724ba493c06a4d9ec7d768aacb0ae7e9168c0028372c10a7600ef9e464c2617a6185c7f9f1517cf7925c3516a0bf6a8f6e7e229a8fa104e164b8cb328d16eb11f3ea40f968302cd6aeff674f113a0d5f4eec61d57142b4efbbaf733db8415459c129ac5721a1b1bea5a025da135349fef2e86031668421ede6358b63d5078c2bedf744d0b9304603ffa2726d02e8c9bedcddb10e57654bddbf2ff23134fd892ddaff5e13c7571c673578d336aa4cd6f7ca9f30fa7ff2cf4ea874d55be1e755b749a2246ca93b7bd613e24c52ecd0e0190563713f2df9406088e6b2cd92d9b03b4e3affe99b7624321bd6b462fbdf3235bcfe52871605f0cada9534cc6bb07b31b133dea6b0f533aeb70ec48236de198c427080056e3d16a420157d335b08369f69b8897645453e8087753b82fcf8522406be11fbef23cf74e343dd70144cfa5ee7e67ab40a2dc21902315b46772eb4275bb841254da0b779691c5f56122b367f86dff07a34bdbc053a9e56dfc4afd69fa3609198d4cc852a9b2707922e89d1b91ebb02dd602dbe9cdcfc74022e67aa0eac4fd1261d7f338e3f31aa22e69b5d4217900caf7606bac392cb7b46b96b21305ed0b0781389ac7d3e23b955e9358d57eb4735c8ebaad59d1f2349dcc16bdaae8c0081d29f21df52ff42d437137ea915aa96d1e286e1a88de177775f59064fb4a84fe06aa67f3a50812921752d0fc249387a70a15aaf42811dcbbcde646c49f404e8f09121fd977cc344c9126d5a8139cc5afa4c15e555805e2edb6fe4a26460990462fabe10ea395785faa72127618e810bdabf626a3e54f78cda87a4d3c068c09e10663bc532763b0861d40cbe1eb9e8733ffc701df12c7c72a824b1c581aec615a1041c48075e3a7ea820617fc48ae9a815b01b78ca7dacab62c9f4b32028d8e0e0d15d296790ffe2507bfab1653f0e13bc3e70337762110b30e0f2e2c7a86d2ac0bdeb5e0f0ec18899aac0ae393b62e1727b189591efbaaa3853735ea9073d90115f3212a4b7d53472fccd625def7ea36ec894cb964a6dfc9a7ccb369d549c0a125b1f2f6f5fd4fa21b90dbe622eec58efbb1705e26cc444bc5aa0671d1b68d961bb5706653c96d8875eed434702544d6381308c43e8fddb0a5640340a64a287b20ee4d26b56ebf145ea85a4315980f5c1f4e64f97d3ee7269de80f1d5947c1a61fe375768d34e3151255e55d0f6857e895d118541da36e13ebdf6eee9e981dcd0d9b0fb6f9fab694ba9973b04780ae2729596e3c425dc871a404a2bc36bf5a7300e0cc098986f2091345a77f699bf2eb03d648c8f8886e8e103faef891f1f3430d57c6d441288ca100d3cb75b8e3197a571fef30525f4b542350d71170de7b63197434f4adf3c4383e89d67c298c03447e65f8b572898d86bcc8df7d5df4110815a16f3839c72f3ac89b895876947140d4aee3cf1d7ef24a44d41703097a6c912c783b7b2a2d525b86be5b993cee26e637b892bd6233a46d0412808ed53309a126051ffdd0d5e0ba154d1f3580a24c5896d172b523c26bca9f2cb842195ff17803998f55cb444e663539aebb346cb75f5dd2f096ed1989947829c17059d40d3121f726cfef977699f9f5f4c66b9073262f539766dc48a053936113d6cb284ef3182d0a64eadcc863d7267978ca1d1afd73412e3fddd3be739603e57cb3ab538b2daf94efc9e5d3473bee3b714734a85db1367c08b05bad992d964802caf16e360e0e85d8f672e27ade6dd0afcfa6eb56f6181729cbd29af3e38b3b4ecc8a4ed724b723a3ad15890a0520a9cc26af542c1092d15000e9f050f8ace54f722fca9b0fae98c4af61f623dfa3b6547dd7c7f269e4106d91c1fb7362b98c1d36f73195116ba3d41cc2bb48e150c70aeb89257d4830e1c270cb936c127a23dc0c6fc7f62f8d6e4417046d79ade659637b6ffeba9b4cff1906a2088e5f6a9609aa704f9a1165b54e18ce8e325c8671f0a81f9263ecdc7efebbf9b86dd008d5f5f7cee56e1c021e2fe60ad190defec566a5d0bab3da01faadd8362daf771f33955181342d11e1a24c26b5d76a39cc1cf7536241ecb9ac2b798ded0d845d1a27c4630316c321b3545192fe684d5ac406677e9bcb389409a0bb05d0cada8421c5344a8cdb750575e263afadfccb386f60fd58489d0bb4370f58f4dc12936a0c1f7d94e2faa22b29adb0e175cc473c2b2b52586dcd28eeb8a244cc8cb76d5f6fa9ce3842a7f61c3e506b19db42f1e4cd8a4b05faf174375a49a3345d08b0c6544bc814d411b21b8e0930d8cbcd0a0b12d146fc1706a84570251f07d63229e6b54cedfe841b371927bb245fea1098b2d4893abe28bf9cf892f53e29787044c915920868935d1005f86006eccbb4c753a380a3f46ac1467db33834c5acdff2c04978e0457583532f0610d3970d4a9b5b3838447839d1fff0506f28421b675effc4974c3bb02bd22bcd664be35d9e5cdc1ec748617d7ffce93e68f835aaec582277ee1beabf7abe2c63fc73c84ea149425c4ae255b6ef597aa1daeb19f6d5a9aec78ca0264ed51417192e91d71dd8c4b1687cff223ea19889910882de0c4f273b29045dade333512c37500afac03dadf9007c95e3ebe1eb027df638d698a535a9ad67172499858e2032af8d2c6452d9b4a0f925e285a6d03775ad1ef89ec446adfb664f078fac8222af34ebfef8c8e5c5d07332510d9be767a2a8fd3f63fc719180097b492a444a1a6f28902cbbdd05dbf2624db029395faade8c1648f360c5bcf6f00969509f4e2fd5532d79e87172cce7840ae5b302068c4e799bff095a94ee7ae5461e52bcd22078a08923c52e18c127deb635785ef81c67e7244e64c1c377396736dffed3ec1692cf5b8c4f2ea3fbe22fe38682a641486f2c458d951ea9d5fc55680cedd00803fadd8ff3aa101e11c264bdfa027ca68801f1f4c873b99f1f3b4a40abc376a800d97f88e29c845d8d9046509c88f955f9ca06da4815e00533890681dc0fe6733020b784d02b21d43155fdd5073bbcaf3e1566bcc390a9ba9b063062a48a9f3f2a5d3bf4bf23bee163709fde91c96de0c5a68fe89f2efdc3ac38fcb7c1db40c61a51a42e9804b0b1dd73cc84648a2281fb4fef0cac1feb14770e9b801ee189241e0fad6797d640cd9cf2aa1f55f4907794b6a3e4090a0074f1649e835d0f72352f14bb17efa94ebba89f2cc195d19c0a4482ec507bdd852b55c79e9d8c59b6bf0ada7730dae0434db913ece0a06828895b07f39a3e6ede7e9b2c0e1466188e1313c67ac6c4f1f57e5f5557f09fc8aa8d80995ef0505ab76c174d775f52d340d313f9b536af11d44cf8cab2f740329ac458c884671dcaadc9f91629eb57fa5e220f89ead7fa2e99f3aec56bd785433322b26e444bbf3b9ac16d3fbdbeb5caf9881cfe4342d8f74ddfc2b008e5e7f36a2441b8f69eee1c4b9b5920e6cbc1adf92f2d06bee425899d3e1103d87dcb7d890b87cab8fd75dba3d58da32f6b43f6b72a1c68b90a5619cc74439f23f6bfeda6e7a8bdcea0174649192ac8a3077f36e7212f0ef21644f05fbb965313c6b59f1a3f0e5c1eb66a2e4d73fe198551733e78562bb24ecff59b56bdfd4c04ca5ffb49bcf1d8eb8f47561c76454dd44b47618b57011a491c2c7b0c8f582b70a6dbd8e973244bfb585aeef41986969bc766b04dad2b9029f14ccddffdfc94c9c51aec7d3098e666ae5a374071a1d0ebd51e1a2b0a105b7ab3c6ad17aea375f660802b5fa962f9d7c641ade8f21d8419cfe672ca5e2881b2d7d1561136e96ca186e48709f2a388aacabe3793580b722c8d112a0bfd1d0c0af75def97d5d4c73a5cc62be153242459dfb1acf02bdebf66775fb3c52326917db73597a5134cfe5c0aff32db8eb8e8800ff2a98b25d2693445b93b94201097455751c08f78b7f961ab6bb7b72286b293c77d219585c66f12c49921c0e90e4878e1ff88252d4728c52613de28c758fa1fbd8fe1c563055120ac8565f85f0312a3c57ce1e84512a7e6f01d38bb3649eea2b549a89f4c7c9e8d048c6cc5d62fc8399a0a6f85477d1a11355dfac6630d01abdf9ae7612169430e0fa9d77c2118752bb4abc4768d0e78c8c1b258206011ff5114d300e5f9cfad20895f4ecc9a0625adf0a0ed53181adee44f47cdd416e49d035feff88a30d11e73e404098a0b2825d765e0c53f5ed8b6b0050ab489e5524616bd7067728ea8ef25bf42ac3f020e4f9b1e6deaf12a3336ece22c90458e376cc588ec99ad2985536bd510ca9f600f856a8db2170bf53339701ca57eeb9ba5bbbfcb45d3345eac67b15a9a7d845716df6841e09c71346046a67fcb5b51be9129c2d0faea4eea5f7af7fcacdddc530aef1e9d1981bd9a78af2a0d502bddd3748095d619210cf3aa8705c8492e156311474629244272eba57adaa7e846c3fd8fbbaac71b25bc3feccfa94e205e8fdb02e93809f7de44c8910070ecb3ab1dc6e088dfd54655f84063e4ad7caa07870e257f213cb9ba4e34e721d51d8bbec887896bb40502d0f4646bda6254059093df14e661cdf5d9fa1a86e2f31eeaedae100990227762b9f2d8ec60afecd697a2d02268e342042b0d69d47305455a40599e01a483736fc1d9becf554c695b07a27a27bf096c3282aad2d900c6b7a8fad55643307493f8f5bdad4c3f8d723a97c8a701fb4e51bc063eb1d8bc47ce98ad16ce5b5ffd15b5f7074ed14e9861552e994912401846c12d1ff7905485680f6a0a49cbf4e970e5e141863a92f64c2bfbfd1a1174f603d2a3e484cffbec5010c3525dc2c6c8defe0c84bf8fe3cfeaf297ea30291d98fa5c334b60f5b157e576a25f1bc5ffadd50da6da10933815c8c19605ed2cb08b7a1e4fa23741de312de8b96e8dc72c45770535a0225522978f46cf1ed21ab5f36e2809661362bd7d96bf9a687bc852ee38c59529e06742092694ee965306f7f63faed26003a1b3a0722a267a68dd9d8336bbb86153a483b6be5e6d09c82d52f1ca592a4fcfc91aa516c7b437d24fc0cc47ddd3c783900840ffb419c37d28cfc87e9bf208439bcba9dc1b3d8f3a8742bd2d40dfff4c47a402f93cb70ee62ac421452d7f50b41ecedadf39bfb01afac4290ccdb2c506570a3bb837c133ee2b7bd5cdd4f38fa55aa486fffa202bcf0bfa43707f66d8d6dce1da6e9f42f0440e81894e21855aef5b8a1099064194de5d0497407309360e77ab62298cc6a7a7171617950855fa6e0cc6d3b58d131d266a956d1487a5e73347d133f3edcd09f0298a0c490d03d5fdf00ba83fe139d3b3c0eeefa89e3be6978224b59a8a76751d077d4aadc1d2c3b7b8b03ba7c0d41e798665b99587a1349585699f793e882c17151d330f5a5bff59500819e8e6940a17986979f5806dd2f482ab65d5535c42510937a1e876afbf69f5a829709008a489b9424cc64a5056a6aa6940a88b8bfb1b0022da31d46a05fa6e2fe2f0ca37b71ef3294c374da7f869f5c5195a2047f2f52c1606a44856951fc2029a1c1144932a1a8347c3178f7353972d05d6dc04937816a787ee7a67834a972e8c73651955d9c2820d0941b2b9703b7517ab89cddbca15c281d195c64d5968e0b04f50d10f46714a669d7b5b44a498bd3444c78678d219ceda4cea398aa096457e1a8c148cf86c655ca7e1701d895f3a6c35b9e43c95305b3ef3d50f8b2ccb50a77f64ccad9105908ebb80a1b8a2f6a4cd61fe25c453133f46da139b495892c89059549bfbf6ebf55afb5a56936f9e0c4deebeabe56f1cef15bd45d2c8c4abde84c0c5335bf56f1f298106b3b43a002acf52f1bd330825014e510e11718c2925f353bcd7f7e8b2b09b57e4e14ff26b88b6fd0a94dbf308f688b7fe963cbb6669ce06450fba3bcacfe8922a30dfb5c4c43ed85062969f155d0fcf9eb57301a20fd246a091ddd91e58f93c527184325ebf94c262fa2110053485c49ae2de63239543f5660d218f531efaeb5619bacaf6ec1a531a6f3e4d2956f3fba0a2564c97acac533b19e50c7f1ee04e0c99171bb2dd2e62c3ff384f10b21e392f556dec389c7004e57f81976a8638172ccef2a2579de9a2dfefcb670f06a188216fd3507683b4225cabb350fb4debdd0955fddf0af3edb592f89d5321f89e21848a664a152a2f10cd30e5c3062a73f01fac6d93b18df54a98507d7b24e1a17e79d5cd7d7a04b6cf687f64a54f769ee6c3e63ea939943ba3ad71ba7204d2d142645247e71ba82abd6d8417e3e2af10c1744f09afbdb83348b8df388005fa197a28bfb1f8888fa91ac2f37e7c50740639428decaffd29f3d04c45736f22ba59f6cc8c9b9312bdaaf2ad0eca04c6a8ba9e666243cefb2349cc162922e90e82555416ed28ccdf080cd47acdab87e3d04fe234f5b61e56a14cfea2ac4d82142458d38c7226bb448488b603508251114422bdf608ade285070d3fb8fa19be1efadb5e98dba8fe4a0c9bdec550f90ab2bb34ed5fe5ad599d3edf27d5a35dae2aebfb3d296b07cb8f99c4a1c206b0b6b59271893eb9683fe948e6d393664831038ade85996477320edd744404e4bb9f874a5c13b49d2f690a5decca2551ace314736f8cc59be9a8275245c14730b58bfb696f7b903f798b87a17aadff25ca8217d71f6dc06f7292dc614651a3a12e26d678f12bb20f594e36d8bfd1eb61e6562221acc3c70b28ef652043eaeac624d8d1680fba70ef5f8d6e90125087ac8520075b5dec10f13ecbe75a01ced0777297615fba720f5bdf4acf77fbb61fdf6f85281b7cb7e98388ffccb8455c128f6c741d1371e4eb77ff31254000c5c7b986ec23cfda8e2c9d14b82ce9c8786199b8fd5803c96bca8cda0c50d51a31f971ad900d6fc92e57b5c54cb1628a4ad1caa3a5d00dbe25028a0dbd729902088213881229d5a5053a0191ca849866e654f9bd5ba96aca03f1f4124ce3d62b30f4e7998627f297d970ac0b1b53d1d6f6cb845e9c5ad21900150d743eeda160d5ffaed2ff2d85c43efe8d4a31c58ab21732c303232c3878801a379941be034b86223be51713a14bc8f59b8f103095c109e1ffd7adfc4e6dd8f8b6a6aaef1533c482e984b3267ab716b1185ff128ceb78a0d6c98cab05593591c276d0a646c2e4e0d1df6232b8e4a6ac844c7e6eedd02cf16525b9cf067ab4297d849911d9fb588bda630212f042499036156d5eca74d67ff132476867da3277ae180ae38530a23e2ec291a03046c5c50343b4168477e8b03995fd4b52921f85905314d69292b0dd5bacf3f1dd8a3d3211d6021a975cbe496fbbb92db2d214ccd233f12af8bfaf2593c5475177e16836f7ef420d8dde0217b20aaa70b79fa8a173f7ec8cffc3f3d741c6ec11a097061ca3fb6798ec89e5465d56f9f6c56a0f34884f5fdbf17bb467fc428482c033080d26b788806d4be674826f6cb45272eecd195719cbfd29456602e931404d8e783245ccaf9ffbc85e89bcb0a960ef799adfd5306f5a11ff98d386c580b2104a6cb3d665f49103cd770f2953ba77ab813c925e1d3d1befa01320f989200", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03611521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da03219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218625eaefc46d4dc1d14d29441b83268d90e246af546b51272b038fd206edb2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db7296995105557a58df75d3a42759d29e124a15ef43bad6b7fd6a9d93d4efda1c900382d23bccbad9ba827179293d23b719de8374589fcad5bc6d2148bc787593a" + "proof_hex": "0x00000000000000000000000000000000000000000000000ca0bfc9448e9ef16c000000000000000000000000000000000000000000000002c6fc73ad221ed124000000000000000000000000000000000000000000000008224c7cdccbb18af40000000000000000000000000000000000000000000000000001877d7b0fe868000000000000000000000000000000000000000000000004ffaa9844e438d4d300000000000000000000000000000000000000000000000ac1d3924637ac840700000000000000000000000000000000000000000000000b23147b92159485ad0000000000000000000000000000000000000000000000000002e033f6bc040300000000000000000000000000000000000000000000000768068eda5eb7ec45000000000000000000000000000000000000000000000006b320ac7697a7d933000000000000000000000000000000000000000000000003ca3c43312f4a1a730000000000000000000000000000000000000000000000000000b90e9ebd4813000000000000000000000000000000000000000000000002d7efb8bc1720f5e4000000000000000000000000000000000000000000000003f44e6bbb5f7dd5e000000000000000000000000000000000000000000000000d73b5021fc0617d170000000000000000000000000000000000000000000000000002138f4eea500814ddc04dff7aade6906eaa66607c4f4fc4b7fad8d7d6dc1781b1ccb5824ae1460e35805f5cf672d03d0c6e86292f9270e308666e0a68e54ba34c48c7d1c1171e08ab78f3b3a380edceaf2be1332bb0f6970b24f889216fb52b96903c3672078e2a2d8c18c4b84f1ee541265621917acd4fce05eec99dc817ef9a1b30df545bdf2ffe8839de28e724ff17364a02a9190034ee4093d0771fe513866af5eee3e16c2640fd2eed50030bcc39cdfc2089b3af2b2fc2cf47c558c544d264cb316924fb06cabc1c64e480876c03a96dd26e2ec80e2a1fe4a060eba855017ac571e27bf226f2d314aa75307227826f8c8cc3558237c0c85427b4053190b5a66b8518499a04c8896e47bde11d94d474f7eb7e24a697ff050a323e51605c0a2f3795b43a55216ecd8b7dbdd337ec0eed71fac7dca88403ce3aad06ca8e1f5804dbf2dbfeea2d01dd0ea69cbd98b44cd4163166c47696b0cb64815f84169ca91ff184bc2aa703873167206ba8215672551c6c39711972e0fcb6171286004706f3f61d7c56d50f286c3eb1632be8786c44dc777090a9f3b2528a91259674a958b8287d9e6e7902eda561c95e35960e5fc1a918c47214a8a3fd83999188e9cf8c4bb3af86a867072be6dc0e4cf33e535bb85259a027f564bfcb50f2633630222848a6fbc30bc81f15de7cce451611df563628ebf63c866506617a24c47ee9943936bedf6006d403e517ea950c046b99c76b6a2f049de41634d85d23ec7fe432a42c24131a81e41e87bc342ff0f4a94a49a0e42bc24fab0ad8569dbe45de50452d2f1518f6964d106282a56209e9549419539fa23a7a21d5adb558af735bb87d547053b4b077b328e1943bbf60c5f59f31a56dbd1bf144efa369f33bb0c50f7e45ebc416e850d2241b4c910649052f98c615c093e763eb742f1cd429f36c4824b3f875f18c45581825b2a7c498228972f28cc96b5c90ee1c1dbbbc46d9ffae4973bccf711fca6d17a07d52ec5c6aed0fc3e9a064c37ade09de5dcae4e637f46271f943e8933c50061892e9ad08e8bff03cd7eddfc9ffada674bb27d9867e628d90ef6e9fd19ee92222e6a3072e0948ff4d178389a49a145e68efdbf306e35874e3ee27452ff17028fbcba83976dfd49e75c8bad82fb9339e77aa7fb76af7670b90e084210ebcc223d988bb703c4a113f7131e52a583d944668b72c08fcc4f552a6927dc3c7333b06b41b4b298fb5caa9170db380fe81195d0da8c38d6de7c337c52e5f024f046526d3b8bf4befc00cc172fde6e835ebe761c7af500c1a3de202df707e91201f7a02d38fa8b9456f6777eb006c1834afae72b3e500ffcea21a36b82e83a704da1e1863aaf8b58a8662f0dcb6fe6fd3e35bcb4bb922291315bcdd7d2f00ccc7db3d20e4d6112826184c37f83fef8bbe495aa3976e2e8c334b967ce4fc0a94d26503189d92a2f7b89fea1bcef0c5ae1147d833e4ac1d52120b36cb7d10619527140b27a521e78a3d4075e127c21300306256edc76d094502551553efcead0caec3b1097621f0a820ca04263bd57e7afa49cfb92a50eeffe2ac26f7502a496adfdf8e23d183479522e4fd838ee09a8fd50fec4a22103536fb118a251999b16042603d025ddd4670df746903205ec04aaa1c131a8565298996d2ef8ae601c995ee0e1c27b2ee13e4555206067c20ee5e054c793933fad112f9cfb4edefa8b6a5cccc8a01fdf4cb41e6d9bf4216246445428daa66e8263a65b79139ab1011ef9a150b9b1a40b8798c1e4e008ec02795bbf95bdd7a531a94ae8df852e92b98e7766fa3ad0c79d387b21791f763ca553d7d225632bcf58e60c6c608ee1443f98f979f9d3603afa7aef8fb5c103ca78c01bcc6917d4f1e0aa09bf3ea417c51c1c3f9ea2b902151931b21c9d25e574019fb092e03c40399b0b8fad6e3347f5961674c366450049609cb8eae6ff4e987806a36851ec132675dfdd390f61fc185b808b31af6ab220359a2710339887cdb032a26cee0dc574e33c1651f7e8b746d016f39fff2c605cb5e7f0b7a839277f904d2bf506ecc02f74be5baa88793d3265700bf8801312174144bf5372435bebb6c3af3d31bd15519858190765d99eb03caa1b516aa3a219db366834e6cf2d8dd47bafc50fce17f23a9556dc30699f727f09edecee94d252a7c4d5498c27a3915b7ac677625ac6829b5777a2dd234491d87168bccbf5301b9221ff82f1648b3b24d2e107329c0a910034296426f76ccc9941c1267df6d14d180edf7ff58f8aafc9fddc2b6a9a641e608c5d6ee03ccd615b645b17775502ac1b479e0010f878c96dcfb913639debcc8dc163faa63994d6fe4299db056f125cbabf7fd62bc792e6505b14e5849ef4506bb43babd4cd46e9df6d402c7e4de2e213bf9ca80aaca06b1baeb5554cce579df7cfad676200cfd2518592c92c6810728dcbf5a308018bdd6a9d0d6a3249cd59a8725da15de0a96a899f510c5407711b0753955e733c32aa3b780c292dcc4a8a24ff2f7bb224370ad0b0c6fca1f601947e99c3cbb54eefe69676eb12aaa3473410966221a4980511010e6f3212c281aae2b27f42ea11fb627fa4c854828ae3d1bb27551fb5ec75b54e342f5cef48f0d592aca51491f88b890e7dd0aaeeb290d165a763aa3b7f3b34ff65fa5a05f8f014f6b3daab5bcefdc03d0aa1f38464f5b0791d8e1f379669b72f548d9afe3490cbeeb0626f19b784265812c1f6161978d27f9e9c0349f028fe3b71197ef130c2dcf2eb7d5edb0b18a4194616eb35c7d614e26fcab690613d9757b323d94867d0da5034a534f65584cce55bd9c5119c20d2b8431ace83236e90ccb7be0c387b11b3c916229a3bd8603bfcb9e293b7c9a2363b898382d57eed13d5ed795066b07218dab59a3ffb999172a0025c43d200d12d8a155128c4db831c24560f654e43f1a27266db9ad1d94700efab02e2f0d39c6e2a84f02523fb12d290ba50e9a1a9805b38dca8bc7363b838f11fc5fcc4a39acc6c72112807ce045be538f43e9f27c01e75d05c14f7596940ec764feb95e2e28f10390a0e74e5cb3f16140127cfa6a156fa560a3c6e13592c8cb5acce08db1c9874a24e8a9ec69e453fa9f1384d46b1f263787814072fc03ea72b8abc134a205c058e5e9b963cf4b4b043993a01abe01f4245be8b9d7f9f127e5c248ac6c5b5dd7b3454e07ded8374360d40d75a39d1d609f99c2bcec53998e407d157b88698bf2b4c830874dfa537b5660303e798d095fe35d6c3ccbbbd131305128dfc78d99bd02faaca0f03250b0ba2cf8f859000afa115aacb761780b4ccf0eabb203038ea0ba410b2444e91ae6a10d0a729763234c408b6cbaf328d74d6be6bfdd4b4bdb74bde0e49da02911bbac860f7b1ecd27981f9ba1b18d35d4db16012579d470cba0e5e117421ac0da980fb45ea23f9b1f4bfded7640d9a41767a3819ffed3d0b1ccff98ad78f63cb4548b6c487e3fa90a8e68436d8db97480afb645de0e76939f6aec3a9183ccc113ae639c4f445b4c161e47279af3abe35b80d6a971ae876e0bcdf15ddc455da007ed56c493de38182b07e1f76681a2fa9196f45c3d25f98606688b55ea6336e19638711ff00d788f16bab3baf7545061ef03f59fd3b3c69a5da63683b801868689aa56c890cba7640b3ef1806aae00be03157081b188c4bfcc045ddd65d1364bbd2f73d03d5eccff18cbfd02fef55f8259a3abb1cf3c80dae34193e155ce2d02117b2281181acad525abfcef8f617e08f3264d0cc3676b330037bef0c76d2be781d9109130dae3a2064dde40ea9103327dc858625e5c5f43636aeadfbbf08b766607aea64758115d12f3a3ccaa680c8bac7c90675b26db2aa4e437820f8843f613f9f1bfd8cb4a431bdab7ae3f2815251e6440f4575397b1362413dc0d908ac8035d7e0dc154462102269ce752caf1c0e965c80463937edade5f71dbb16559824a082fd53c65483e06fb749bec6adfe9659772b8db6d26541bf074d751096ad81e99c52d7ef4102c2646c0a220aa8b25cc7fff384b029e841c5e1d1a9663e247c9fb35838a44322b00b86c4f53843909921594d8772dd67f6fcd57b555453ec0e6a2914fb82b4f9e2fa795c99591a02f6cddcbeeff3995a26eff6d038b7c063ce6b7e95f591130710c9a83eb1d4092d30b6e3a0db2643d049eddbb6486ca3ff613045343ae99a6af052b868c84142535fcf42cd31d9c039d599e1492cce99d4dd8439330b0a2077f165fc7e1fb0a27e24eac75faefb6ebb8b825e535423f5663b09f7b157205c0302151f296af0b1d5734e1e2a8eb3cd397a8f7e9cd015e333ce73c33df4bd49e860bda526af203f82dc6b615c2ebbbaa3b99a13e45f231c133bdc755807b03311123d395bfe1a13422c38063cd2f8cc6604a3ffbad19e5bbd5776668f5b907c3f72150da121fc376b7231173de23fb4b6d2edc5989dd2e82ae695cce929259aac229968d3d266b9913cf2d577474ccbda946ddc4ad52cab0388806f364246982402ca8b651f56a0f593424f4511a776152de94b2d5e040f23eb6f50d03029a0731056d6bb00954510884059b04e580005f131b335f45dd627242e573447d24ca9317ebaa00cee1d6cd54e10693d4656d19191ea9d63f1fb58989b317a7bb4f173f02154a9ad7593c3f48daa36a36deaf60a363365cadefcb3cd7f7eedf78e07817168d8e60ae9532d27b0eb422367876aa2840738a06d8487727fb74a6e75e44f6045b19a874d8bbd97efbaf376dd343648da57e8da19a975e3c715af62bcd12cf1b40e16223afd640866cefb3d7dba53e2e6a306634692d558fdf6ca8190683c8241d67526488d47450901eee756a9ce807cf18f54e62cc823f5d47b4e3af46ca1f3b94e88bd4115fd91451db59a8e8dc0c111024a2b38af1deb3854d2994887b1037817024ab3081acfeff6b50282ba402d85b56966899fb39a6c01df6771f33044b41a29308e89a75b06b6627d85cb028d191f46bfc52df2723360f52662e99087a5737c61ee99f7cd057bf2e18689f5b747595ff1199ae6798b0499c8d75e113362ac3d589042b46807e6fbf16b6e40c8f94e444d7652dcc44d8984ad500cf1d29a2242bd30bfb5bd3eb76e7cf673275c21f9ecd128d346760169d28ab130b042b4111792bb700688ed4c313c5f168776bebd4efc7b5c8a244d8ba035bc42d039278015f319ef204e7869582a42a617cc51566356ba50ed425e8a4b749d81a0ad6f18abb1f53267cff6f65e6714c4a397efb80184e037c6327175f09bcaf7e28e8c9aee1ff608389deb8627134d0ce9fa5feb55c170e782d5214ccd605eec1257df978c84c72adec12e2b0f3126371d1ee637532aa455e2d2a78d62afdd7180045f5626941bdf1f7849adbcb24000d1c9eac6d91adadf387706d72b5dd34e804d4b52193f0dcd0d219a9063743deb5f4349336dbe6b49dbf416c1789d3d07d304e7354d6a54ead2ec2ba06b67b345473b6e1a9b1c26156c39facaf76f899e428ccb16ec1924cb31fb117f22bd7b131cbd6ea732abfc50459469e76d07dd90f1387549c3b417078677264bf1f284a099584aac71c07e7fb7363c60c868f435d20a32b528b59ce742f5afae859176c4931b55e6c0fd7e3b34fac1e1fc4116fb61da0d8cf4cb32d83588556adfc358e1130ce98576c3a350612bb36da466e5e430cd2ee1202e4ba044375f6bb5082a688a43cbc32fa463585c9f90a8572910d9a06c16854bbd5268dc754d94d49f89542e8cc8b2ef418ceb21cac5bd74938c7fa1da3952a91f4563cfccc6eb1946a3ddf30c389085a4ab95a232b732e139e86200d1a9770453f3cf095f35f9ddaea29d99de184b8123a5f2b63c0e824c16207991ad29daaa0ee898629c92a646495916aa6fd424d5015b85432cee703ad1b2aeb038313c5e1410d79237e6ca180f40c19d5e5b2513a1377ca2fac6715217ba9351fc5b3942d314b2e93c71a2f96d9155db47b6d78e1cc2bd004aa22003e163b6412a752ca2e7889cefa2751358fd0fbe3884d99f106d68c973c1dce717a0f33eb02837031a40957b5fa4835b44747471801e52586ac30295db9fc1bb1a704f18b0d1447924bf9f1e02784cc1b0532824a4c078107acd6fd9db620fd1a61a0b0ee0aaa2950bab91529b7454b666079592dc6e88fae0ed90f90d35892225d47a4862f8963dea3b555a8f01f4da08bd46ae1ae6bc16bcf7005347c8c19de6c2145ad125aa502df4cd04faff782bc862e046cc3240bf2c8bcd04f70bb4ef6d36fa27c1677f6bd1cd6b98983ddf5012b2f031398fbf6ca5c8a32da32cde2e62c75369011f86008c2d4c0ca000b7ad4499d797d6b91fa77825fb44045e75a9216249177301422c032ca94ed2749e37ac341bfcd3d25fc7ecd745e66051a06188f3f4a0b07383c49c3ef429d01566aa613cf191134cf3431342729cfbaa7732cf5482fd72aa56ab908372599856c78c0b74632d5d60966806bd67d0f82f96c58df6256021c260bcdf988a10981f18558f30407b89644a7ea76d78c0ddb151d55530b667c1343edabed74cd778cefe84306c1eab5bfebc5d97b7791300950279dc2aeae2f235b102f73827cdd964704d11d5240f22d7da11fa6afa07b841291a2c1a7ac492c757990536725bda41c4083e8acb511abb6eddeecf6e50539c4c011b3d1d10a27f1b8b43e24e1a85a932f807aae6be08145cb67c932c8a20452d5cdec07bb06169254ee048b3edeadb4c9934efcf8527debbf850f8903614da37e81f52853002fd4cfb00556968925d6b81c10197dae197b9ccfece01494971e15967e157e662a022c3517eba2f66934d09cf0610075120f622acdcec3064a52150f6d095a2724e0d64d5824673d815ea1dec20dbb5b193705c78f2b4415be9e5c2ce67e2d1d0846cb6a25cf5de0808ee27abc17f5508b07f104a3f3f0522b6b226f4b7b8b8e0e999218e897f2a6d36fb1f2dbb1e4b57ce685332a56be535c6c6cc5d257e9ff05a455f3cd0d1f8ea76967807a251b0bcb3a1cd8da51868169fc2f727c528545242850473b17de1e572da6cc3796b98847e836d80bf2ec2618287d37d544866004e27ac002f00d5ff8399f2f6c541c3afde128584ec229ff1bfd92b8fb2fedc826a12118800da2ff24c92d5f7d15f409cc23b8cccef62cade5ec8af6270aaac10e4c6b05214f6fb1f74325f1ea5d67238b2789d21a634c24e687a7636b7ce8590d2c3adb23cd6404145afddbf5e18654b398ac0a0ee354b6cfd254d9971412c302b57afb73e644896ec74bdaad3a21c268c48b37a0cf7970a22e14a95f72addf13af72ee85e567fb98daa6d94ba88d6c8af8e02bfc037fe0624c438793677591146cd48b3da4d2330c7cc82be58818b5c96ea094be4a314c8c1c3023340d48f417fdb66ff71bf86748e95ea77dfdcd27d5593241e172d745dd8ca6907f47e6b207103a1fc9bff0ba9cc5556127307bab4dd8a9cd71f62b579ae35854b736af3d2e7648230877b9611da0c0e657ffd48e0e7982e1489b6a9a37943e6cb947bd2e0abacc109c9e01f390f28eb2bc2264e2e865c46662734ab8a718d3d812c8fdfc04357462790a6d2f5ad09b11f9c4954bb8a61e7f7176ffae8ddb6733acd31fad0b5087afb2695f9eaea944cfba9505200d4fe8788a724e4aa60b165b54c35cfc2ea275976325c84249e203d793ace9ad8001a255ebfae02f564a79ad3ef9946c2dde6205a2c3b0fa8cd8e0b431bd3b0ff968a9a97cb61f2737fb6ef216ac4afb121d720587325db079dc26956af3712406d0ce60be27365339434aa7b9ee129001b0515f27e5434b9ecf0e445ee0a45d3ccd7774a54ce036ffb5177730971fc814a28953fad3b536c531909042ffa1677de8fafac8eb7db383dd9a88ea4527f70de0966b2d3bac048cc489c0c0c691913e79bc4f3ba6ac385d012978628ad122098600e73b68eddfffbafb4022683ceb1c5d689808d8f5a5fdc8ffc4730eda3426a7caf15d866e5284890ce098709e86666e178cfd0689f4d1a747666fb7e7ac084cd90565505ddf40fc09aa3e8bfe26c2db1432e60f0eb404aeea8e4249a6960a303c3fe143cb55e45564fe5b0913bf8f6c982c4c1f7649985707ee9fdbda1712afd04b600ab380d70905cee858094ca77f0b8a595709a670dd57b8faef6455179804b0479438dceab774cb4d960609046dfc54837771e26f656d613a1fb1831c84c7318ef1b588cdf7d909e123c20359af82e3d0a9bb9d1d26554b009804462a6934cef992bf0765f824d9986f3aeebed385f8731ead30635a6ce040c50618089e2e038bc669e32c3edcd131748319cae641a697f5566484a5b5504973d80b1b3f4cc0d1cf90e917b6ad033ec67e2604689987b38edff98d9ce4bf410f07f320b79c6e4192eec6ea444dc7a96af93823e5f114a0a21b343b8b6ff1a5f767ef1e0b9c2c1e64d85aa608eccc55e23d1c485f9a020aef106f2d718294e71f74ce1e49aab051f13f1142f9082466304d55db810f59e935482b661e03bf7d4200a9033ccb47c4871c61b9a42198ac6093d8d541d270d61e03a20ecad6e817cfc0ef30636283d0dcf39193aefe762da76c6e097f0a739c646ae0aea623666a3c85850c4f1220292cd457719f4071a133247a33eca153bb23a86742cd45e9f662cfc70b8dd504c56ce14ac9281add81dc778f47d84a27cc8d9560089a06c0190f50ef0bb97e18ce7eee5a4b05824fc996d8be0be1a8607a2dc26b9081af9f4c7cdc080ad4f1362d634c656a60a00a633af090d6e88ff387f306dd112205a300be4b5e175a0f41ee2865da3d79bd45526801e5032bd937f0f233f1ccebce99a43140ef234c7aefdd9c4a8f2644a9737d9ffcc545c288e8532d239c5176e230d3b93bf805c0ef1dcfffa75d1d2239524bd3a31b750b503a1e80c93d53457ab6aead7905297ec80f9ba9766b86db4d469ebb8e722662bcb84f4fb56e4030d482e9eff9d425dbade57b6986f552847e2575ae031911abf1a32e564a596ed576d3485de6401df28c94d9f4881155e58c6619602c2a468e27ba9b3df8c12199bfb949cbba410075704eba55021d5b95af43bb0cf783f84504a364fb940de65bff7eb7af405023a786e30bfd564da19f1155cc39b2d3f2a6af67b3b5204687917587c932b06a1deb041c5f0aab75a685874b5e6562c25c68e73155641b3e66e5567a553a04ef0ce570707daaa4b832abde636ff8a551512fd4c098fae9788026a28929ae4f3d09fc6fc052215b7b72f281ad73b3d4ba061286a713532fc71c54d264dd88c40a139598384a5b5090ef33fe0136e29eaa89390f81f3f46c2e61bee9b3fe89ad0025fbd0659972ec4213a2c76d87aadae18718d14abae030c5604ca57dcba8743b00bc5ef116254de78a4bbd1cbe1c0a07065296e671bcb8799caf900f24ea440f1ace537f57b619f0e5af6ab81ab1f229b22e47a9f9c6f8ddb4af9672e142cc4a172c915ae763d93adc9d47aacf9ecb9564e662c30741ec9b5c53ba3683a99ad800cb93318c412ecd8cc68042f1b2bbf47a3057d98ccffa1119780ed2975d2b031a9e72eb5031d48e4f38ab89ec302f2680e5bf6cdecdb20d9b9b3045f14c414a04ce171fd95093b88239b7b7d2705e385c0f598c8d188fb51f46f9f7405c358a2721a2fe172c8f37a88d02c0480694867d40e734f04883503f7acfff6542084c17e4a9834dbbb1f0a1d06ebb8edcb614e50f4d9d69762f94874d04d6fde4c0752d806a1a856674ce00b07a6f0db2f0406821ffe52ee7bb9eef56faf3b678a70b0056caf14c48cef88874a5d02c859c85256e1015202caec5998878c9acc0551d06eb603def682de60f7f0a6b42ee075b80ba4fae308126cd782d7907534fe71912dfd5fff73789282bdabdc9299d7baf436ca341104bc53cf83e797aa7f785101e9d723ebc25c4cff66b835fbe4041916e7465104f9b98478565eb0bfc2997fc0567e4fd5c256c85f68fac3ee24e9319f14d2405076e2a72bc71fc5f068842a71dceac206bd01bc8e72cb790af500bf02e65223546cf33ca170765f805f944070a46b061b5d11b40ace050d8cc5feb16f7c4c9b438eac11ae5981a0618f1d45c12caba43cc1af67eee753b03836dd02e4086eeebc7383e62ff80393178cddfa30efd36409f3cce15a1f3ac25691bdcb29bee5f05aadae6ad7338c675b2c4f96506539f855e4208731d60c46aeb34602b935a8f721310d9bebb0d1bc55a48947829e0aff2871271b07d65be03044bccd3983cc2991e66e1d4fb79f4a45307913326330c5496174b12a1904fc8504adbf956d3d00caf4d042a1c23d426ddfe81de04110ed4e9d82d413131da701c94510acc7894d2c5783821fb1b2947cf9b9fc815c703538175f3c0cf2faad70dda3af3dc6967bc4162e5dd3f398477700f110e24187e1ba95407833ed314bb24f38410df33bcab59155fb173c172220aedb46105fbbe750bfe20184c07aaf6ab485047c7d2fcf738e5519feac385e7d74e472e22b580993c10199c9a264fb71fd89e7cd56eefbbda1cdd63962d7d30107481532c63d7f82930de9f0e3e2c7affb5392975b7d7744702e68b1766ae7de38f60352ca38e67916736b2da3e6433726cd27b7f17e76769b44a03fe3c4e2cad5379c529ab3694223c72b67c02c60057aff7003b3e53d1ec59364a6fc561c7b4b184db2bdd79fdc9704edf0916ff4740514c1987e2f2168cc54b9dd340d02e259f99ce01ad9d5d6944dd52a978e6ba9bca1d277dd21ef3f6928cc2297ee6e76ad3256a15f1009a8956ccd68a338c2e099e5afe9b3365e5818198cb107a9f70be8add7a0ba76731923e8dc81ec699a0ef0fee7451bdf6fdd54f16e3c37bbeb1b23ab07d11a887b6a1318ce8ca89bdd326a988dd00d3b4ae59c1bbbe540469b4b3794666060925b7acc6dab414837c03d8ee123e09f87cf8a12f05c85f3ad1821edf8d12187339266d5d2a9bfa63eea5604415e1d7dfcb7cf9a3df030cdd014c5c615e340cbca315a89dd69f61b352c60c2ac8329eb51b4cacf86f3c467c0199eb1199f7093628fe7f809ceb200f8b262781c164f9e96ddb9ad897fcd1822bee0bc8df832c92537cf2cdfbc0e4d83553deb8249c5eb8bc198f1ad13f00f38e855883a4812c8692f4d277a5639d047410ad23a154c3f18b76e4b062a2f82eaa89035615db2c1f23e3ec233ad71196965522f67120d986e10b52475585d7398575cbc8a81626024b8181e049e4889d5939fea09166d9fe502e50f8e75b8a39cfeee63386890495f31496f1b60165d7afba12d993e5f3dfd22b89a1af0fb254d1151f1b263b2cc367575e069b0d0833cedd493be67d216fdec70fd7e4ea0311ffd0c622838c02297e7a5899d85528b3b21b8128abb3ee080f203fa7ef135170f4eaffc9e1fd0ba8712161528fee2c370ac0f78a6325a1f64768309a307def9f9192feabee851b6ec658ab933729028cc58961923dde03438a74bdd19ae209d65dd2a2d1b2f801c07e1b41644974aa3a9de8bb0a0054e1e4e494c2330029e9121930c07e1bf8045674837a5913cf33bd338e0903b9fb9c99a63584d83fdf0ed16c4174334f4a18138d91a0b174724252869878ed251e5dece675925eec92cdcb8215e5e4962d21989b001f106ea3d1a5143133cace9939d4bfd6b263bb3d9ff7853042f7766118940257dcbb06910a1c5809866a3b2d5d12fcce6ca65870c1f9173ff5c9603622ff578cdeae9d93d70ef9f3be40e103ba76d08fa6093dafe5c1b5590a63b9e6304326fae968c3e34f8880064566f2a96ffbe50721f2b5da52d43a5188cd710b1aef98bc4b71623fbf42b824b15a39044f7c1875e2be037b0918772b3a4b433e22a71b722d0878620af6721d59a1053c50234f4f03ca953a37a156fb6170f0190ef5bcc4e73421f17752d6857e24e8a6adb26f80c34d96bb8e613ce3918d4ddf2ab74e92651bf9c4c7be3eb2ab58693be33a8c3bfa0d51d077d9e2feb526241c058d33d0d04a34025d6513192d57dda67ec1800bca63872ed03c866eb1942db6256ab2fc551659b6a6ac38c0d3a218537151ac2497e6ac5d1a6dce982811fd2c2f72c2f6ecbc03420498f9d2d19c28416b931d31eca2a50bd70286778b5d5b3e07a9fcd192fc5c1ef2b71ae66a29d9b86c423a24c3f3d6fba8e8b6fd3a3ef2e621be45cbe7c8a823068cb6169ed79d25885594c68bb5707a95543a8dbc98c83d2593ebf226e6fa831bff91f121397319f586173e4fccfc25eace77d338669586066c1d07f923cda6df3e64acad98fa0c9bd48f3348d5891d153d215f0ef0935e04b3d439f7ae1db0ba9c09160acf0c2dd57dae351e81a229f10550f78569c69d10e0555e194d2feac7c390651f902cdc61a031e3cec9d194ae5a1abd14ad9fb3155a16e4c965278c08c26a4c65b9a7999685e602dae2f686e6eedae39af4cf3c1991c34e32bf7e092b14238f99eced1da6cbb658e72f399c5e2204cc4517284c2f2940346864a12b66ed991e5d9e54daa380c27ff63265545bf809f1cf6040c62d1dab6c55cbfbeaf1cba651c23b6e4183365896bb6a75426fecb5d7e8588625249f65de21a1fca4895de7b13d96debad13bd97edfce751359d75e90dd0ec69b1086a85f55e71d8c65942a39cd57b3ddfc66d17720f40935dfa4041ac91b02ee2e0b88602eb808552a94e2596819a2bd322aeaf970f0b9f25520c979b5ce334e1e4c41062005db6efa42f26b3afe995f3c612a3207a60cf0f86d9afb2f5c3c9e2b98ddb4cdeb681d7a79a2a9e39ee354e55db4f9489931d684aa43609c8c51b02232d661cde0880715c6d7b0d9bf48da01e99f43d720ee4b6d06cf67d1ea57472ad7287de202f92536196e5e4e7efdc289f2e8d29dae80c0ddd34e43b06b700010c6d2dd5eb40b30f5c1f34b1cbf5dd29a9c6413b404cf7f4d5e44c6efd06ebd2f61b00c6a9a23c2ee4a912a346bdeb353a24bb54066c2b48f603ce10bace6fb2b6b2f67b01b1cdf7418a0aedea179e0420b190977ad849bbf763ea19ae22a641e5416a795908c6a5bbf2fc3376ce7c9a189e4852d0a59b1b31cf12bbc26a43c0540859ec14f887cd35eade20a86905e21608136a7b9225a5aa16a9a55318071120ea66796e1e296f0eb89c9dd8a77ec55ba003d439189622adea8684f9c0d7806f7f6a167a329588ef99908d323e4038d7888fb2fd558cca93ff8a8e360148e0c47df63964e87a2f9e5dc456e9371cf436f04ad4ba531b2560a9160fcf8ffe21c8a6392a254c34a14b1fa07415cb779b4a97bfebf9c856716c497c04ee020dc2ba8db6e64064f6a12864b950b4fb8943d07c192c6b3c64636d04e920242f0b821e1e5e9468a4099bb85d0dd20a2ca0ade28850e0c75a29e17d7ea104b967dbf1a0fa7c3ca7ee2fab640e3993c5034cc9db10e99422ec8393af23fc1244823540c04d3650598e70a47652d82822dbeb095bb5422a85ebe8432d25a4b6c4bf1ff0f6522a9ab211ff1043c79af9f816db1a3ca129f5ef5b11c482f7db11867ed3405f151fedc4ecfc5d689a334802fb029a00bd9764908c84397b8ddc832d2a6ec0489befbd5fed24727e2449e82ac9a1405d9a21cd2f57451b548030bd8b00cb41b999bc3ddb385c86dee4bf26a1c90865a9cc3fb683378cb26248fa87424483120ae64deebee903fba7708211f871846015acd4d576f03d8b04662244b5a64260fded3e599ab32a66c2ed158174d3c791cf146feef2486d3458fdeb49f2d2acb27580f9291d1eff894607e2883f2efd696c8d34ac9bfd4a586b8fe056fa28f6e10aa24e33810e8b61312f869c995d4aac65dde2d094795668d54b0dc219fbc062128339cfb1a949f000a02d46388c56437439dbf3d755e85880f52510c59882120bc9892c6ca86d070eea1abe8c2566f701490a9c58012eaf3b6d6d126927348280e5413075437951f5f113bef1d4df52f41f45df426552b611545d37697ff1f0292dafb57a127f033d599a0e08071988f73608d79181ea1a4bbae27414bcdf315cebeeca215eb368de29fcfb40816567217203a1d1597dfdcd00b5220a802430a9ad88662352c218a6dcb7b7e081ec4fbd5bbd0bc983b48d405bd40db07970719874b5f0f23d5cc8242a11f54eb44441ad3afe5b59faf20a86bdc466d2af63113b7f9ca89a8532daef2d3d094372129a5beb0fa710ad73f7aff437585bfa2f2153d127117468e302cf735c858a1d9249dd51377d0c49e5c5087cf8c8e859e80187fd2efb29c999cca326d9faa50bee31813c4610afe50f2e78e140fdc05227c02223b0524a911ad80988b7784337087a40dd271d329432802b6eac492e7e57a0193fac4b5b1b03a48592cd4211acfdfa0916177c1ad875e3b7d60bea7f023b72dc0b82e002721ffc03a4e8aaa8bca823e3323373116bc9d3577f80eed7df61521e0ad3fb1dfab09b965cfa000dc910eee2e99a86ec7debedd1bfa6eba05844a1a880f9155c742f51e7b96419d512e48f56ba3c42b5d7dec49c3500b16c2084c025f86494d8ba7df6789e6ac2df2131f4ba25d5d554d75b2cdf1eeee4e44d6e421d8f4dfe37685b8da071a2112f4d690cff5e495994ac9c2af79f6d1244f984427f1e6c51dfb2011f9d1154ba3d84f2b23b52897c7f2f4a10820195af3d0be462564326ea6a7a6d7e9b4131408df2c8207e26410fbda6e2de4f8977c53fc0290", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da04570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a7110ecfce55cd472966bf92d95eaa2ca0aa89a06129ca6ea67d7b5c9e26e4925e01de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811088db0dd523e2dda90a02310bc443e2796689bb889f1a91f22c7e910915558a7279cc5790a050ab4e07fa5176b9018996360291d94e5cda28e417eb2fdd3e8df" }, "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000088faa138749b3265e00000000000000000000000000000000000000000000000ab39d7744ee99e93800000000000000000000000000000000000000000000000b58e3fd596adfb3f900000000000000000000000000000000000000000000000000021543697d80ac00000000000000000000000000000000000000000000000303b482f3002f24d800000000000000000000000000000000000000000000000b85b3e53031c19580000000000000000000000000000000000000000000000003e4b73366064f452100000000000000000000000000000000000000000000000000005aceab8040e500000000000000000000000000000000000000000000000f5dc05a7bfc973e8e00000000000000000000000000000000000000000000000a5d05f4eb90bb6a3a0000000000000000000000000000000000000000000000060872b59764066d290000000000000000000000000000000000000000000000000001421a9133d75e00000000000000000000000000000000000000000000000f4780863c86fff52f0000000000000000000000000000000000000000000000023927881880d93150000000000000000000000000000000000000000000000000dbbcf61dcefb015b0000000000000000000000000000000000000000000000000000d0ae97d8719c1b55ed60d28990fd534c598c83f2d728a5ecfb56bd465a081d6618cec0292cbd237512e8cbd2be838836717f3a7d3b973f5b990c5a7f1ec421ace3f47a651c5d00fb5ff8417a44f464db9e3db570a7873bd28a5dc9a6bd7a85067e4836b68ce825c611f8f6ba443658991339a5094e904964bb9860defcc05dc2482f953fb6061c687a6373bdb5019e78346a3d8b4aa6358b4b56c32b61afb53bafbf7c51ff7415b3a062e79c6977d6d8564fc067ebddac69d8e8a80bc352294d758b31d13e6c168803f0edf6007b840fb864938af01a0d54ed66c99dd832480f57cbf93354d92dbe31fd406fba859e22d0b8c21ee8ad4357f8fcf75b97fad9e3616fce00d82425ab8ea7cc24b56a26eff6f6ca0f8f37eed160f45b06fb8a36fce6ad6d74bba4190b1a98f0cf95221281d936efbdbf97e1b85c57587a3c202109bd02b0ccac1c0865a52812ff25c6ced7a77eb02dc8b801760bceb81b5dd81497fa55c232c7492bc6317bcff50690bc531833b8f056eb38a2cd70a6593954e78d9f2a1cdfa67b0b906a32ff83bd1e5d632f37b869871b08381ce92e74ca749c5fddb7ae40ac0a0f51a01d2b5f7a67480c57b125fa84f186fe9f640b7461b9a0e35abb212f49cd209f27b1113fe34b0798d1c95cd6e28ebae33697debef7f295d707aefb198d9a0bc0e4b323afe706a0149918f17bc9ffefcb1523a0debc9a18cda5beba59ceca0ef0267a1b20842c607949f2257f3d70b75f00faf629ddcbdf935d0d0eda232b01ce76a7013707c66fbf745546f19d041fea89ec58d214f41dde90912477de87296d3bc7695f2a1b16e258c9525cabca394907434b9d2541d5b4ba692d0bb486213c2e5d7a4361343f0b069971fa22f76f36817ce137b60f0b5b4f0d1be40ce60e54f03a0b00effca458b4623092ca8a33bf27a0b8e9772316d687261523011c1e8187225b6ff80a03bfb97eabf1a873051ed965f503149a705b147d3007de670c317fcd6a7a516bd50c772891e2bb3315f6c2a87c3cb1119cddc826f543abc71957b0eac5663718ae339d51af5256268d9a0474a403a6ba3fc81d52d89f71bd2f831f8944aa85d6d3677e3cc4876aac91778000ccf21aa14ba977af5a4d10da02981669450d55161ca8f053a871d81b5106abdd77663f1ef78bbb0eea21e2a20aaa868967a5907ad5582638c573682a851040e436f2e6c9733ee4896a86784b2ca0f330604ba7a3c5b0b746ee42632eb4cf982d4f3c36ec1bdd34c8bdba023b0d20997d570bb040aab67abf15b560b7c04c52513aee47c6646503836ed35d6f16388eeeeeed374906d0dab5fd1dbf8e291cbec2a6927e9bc4c052c66a9870730b20ff69314b4f45b36eae7896c08e5a06e691c8449e48dedf97ddcd3d674f0b0887ebb0c7f788fa577d949c4de8991658cb0f8dc1af2af7f58aba794610ac002f87749f9d104ae27ff60d06695572ea506326c7e6bee213de54288e8bcae89c0e5751fde4c6228122ad8ff186fdf8bb495f2afbeb85a95ee5cec68db708475d117aca0d4cc27198e0fd43ab5d1f9d7ab9de2f1a36e9bf112589daff912cdc442ee6e8aec7409a15a5fe45e5c201e3a052f12e53d1a0e1ce8ad6607c91f8e9c503c1f9c286c2dde7f8d41abc15a6ebe3825163ede6981ccd6fc9b03112ec426a1fbf2b27f200f4adddcbf200880be7f5a73d27cd24d09e4c6e4d111fac582576079d882aecc30f178bcaa94f8eb567064c9ec9ecfa99034620d0406025400b7a25dc35566bb590e2b378dbaaaa71fcb2279e5b11c15b1a9c8b286b0d1e7d3d611a84b62c97b13a07825b337b9b57aaeef4f86253f4deb5674dcc7a909b0ba5a62bf59aa6a4c18e6a975776383169d14695d6b9a3d305e9a21e745b2b2839515c0daabcad6837b062b7d01dc1ba60505955ecb38d334a523d9b1dbb7ebcc565300e8ad930df265a9e0f95e724b5f713967a976ad3ff3430d23786c016722106840bc0c54b8ab48d4f17c802901bd97dceffa70035d35e38d5ff183830fe90caae070aad78800dfd19c788ef024bd49c9ea8160ac550d117d074afad668a4f6bd510ef8a176328fd8d356b31ca7ba711e3afe130b93dde15d246a37c1270b6e92202ddd3604ceeb469bf93dde8652ce468c184857e67d38139cd669a089aa6861e065c283d1803de3005a23494532da0146ed64b85e40b2de3b63ce5844b29b1d404103d4242b9372da3ab04383a671bf91156329c8325f09bca871eb9b2525bde1e641d847edfcd96cc86cdf5b50944b2faf2e9117ed17de6b5ddc68fcffd88c81a07df6735796c3d20ffd3a78ec7882bee8143ef052475edef9bed6517e3b52428923971ec747cf4f5b9b176d23a4ed0119f7a43044e08f766d367b5cd9ef5610ab73ad97dc6bf4d90f55efa1219facdcc830df23431f5629b286602d4ae5ef903199317dd1e0549502cb2eae16f20916a284e792e268e3b7a57b556ea3a42e9267e8565bbaf76819760ea487f2b6442945c0343be29ae94406f3bfc0df8fddc08ac82ba23e9335dc9f9747a73ad89f80083c64e4e0ca3c3ec5bac16c5c4b98829528096d11677e5b2add86d8d3cb2ab4820a27e6b200c99cb89696853ef6f65194da3ed8a69e1e02e7c956f04f25d64f6d43196af74edbcb57c753c75bad26b046355828fe4b56d8e7b094de4c5284acfb686fca6769822013917b3bf609de92f0cd0835181c4e1e9c37cde2a31e9b44c31b5b6404a6c21cb03c5e2ba6abe4e240fb2bfd29aee712736279e486f8a437aff7e469e85bd5897caad3b28a75e0f2577163a579747d38a674aee66657777f9eae6deb89fa31e82ff1f2694aa389b0d32280e5e10ca4652b0592d2f88f3f966aff1518c47d4f7c9de34cd5193199900117b78694a8c54fce6ee189a22d323e097b90b66f4e2f8012977f5ca861f02178255d84c899b963c3307fb1e41bf1f9200de68ac04bd3042eef9907817e6771daf9d3a34117c81a9c27021cfc836938d33259c9d272ad7d1e7a273b7b54c0a152b720fa2026885f1f12bf2656c147cd9c54fe4b76f6cda531400c8178ca9f2013c91ebee44600dfa4fafb44468f6b425e50c634a024094f8cca64359515839300d925b49ff62a8510bdfed6de3f229654068ce3d956f0f4773106f38d8d40908648c87ccd1c64a2d1e621164c253ff9472ccd8c804a718b313952b120e2488267507664201bc55514d572a0c99e79068801972bc67b062ffaf6db639d590000bc5b218af736380dfb85df08d376733e5f09f473bb3cf82902a0b1070756502076279e5874329df3255ecafb485a0df06e08147fe1f3f85ba6070df13f7552e109ce9a119487805b843c229b96ec320193763785454fef9c984ead471d8a0400a44f3eff7fced4690b673718372c28a8b6d6924d2d0ffcb7cd85d77ae1d7e542da333e9ebc4a6d3d8e4a18172c4a282c170024b8be7a889a57fcc75c1486d7d1b942c027af9c7f98beec95bae62e76068b236387c0f10d19c4afee1274960cc29b78f66787e9624cc182ffe8553674830347e79f7beb68a8b33debca0791f242dd0ad1bdea9a27eb052b4a873a88e3bd0f3be38d6447938527ae035114eec551dda82ab01c2c8a87c78f8441448d17832161b30824d95a71b06fd78b051c04f1be9ad79e03364982723397fc744222f42a3f9e50a53f14d94cfe4decd554040033d5be4a7665044745238a59ac3bf71264854501f0ef4ee5f670690d814881c259967a6df24dc395e0ec1c7b9df807e2280a57705836b38ddc025f3ce8ce6b22c59fbff66af05881bada0c7b1db9a5ba9b601b487b49a9fa48678e51b413b471c1e72a1591e1277aeddbc21c9e887e8c96997880d93b2865a610c04f470dd771e8dd57f4d99b31007bde7601a1873837f2a88c0dd1b886c1ed9b7b411393e1800753e85451a0a3669c9f9a909035e6455bcf5d67a1bdfc4aa243a17c16d6ee6100c493ceea0130062e564d26ad2584491a0a7e215674f3ce0aa5b68f3e3f80f02fc04703bd2c4ef3f812bf23717525e2dca862c640765dd9cbb0a897166ede12c52f69ad514c2b963166d8a0c16223a50170f085c3cb1213897891a2a4d0d6f19ba1a0b8e7faded21a7be15188a512a1e6dd25c9bcc6e6a31ef31cad5c0cb8e1932d820675400dd30942d518df2005ae322f8f3190392739e0b2827b725ec9c2d6738d204989cd547ba1cb3bd37ababc597f124283ddc47a82fe8a4fa89c181082cbc4ac86b67bf74625314994d870426a846c6024026b841efb1b1758390e713f1ca562c70d4385dc5f424727419fbfa3c6632ce96cddef10b998450a8c6f601c4418da4d9e1e56c4eb86c8fff01aec4955f1ddd3fad61f249b6e1a74e0520226c7f49df1282612cfbaf389d6f9081269abad88c9915f8329c6f7f10cd30fe22326f40b9c864dff34ed6c32d8934e1d7a4f42a2cfefb8680b1ec3ce5c91aa90004db4643e5d9b5333344b28816904858056b89e854b880fe2ab9c421bbe47d2c8583c3567a93087fb8a72181db348cfc076505940124d9c48723976a84af7a24568430053eee947b3d1d420d1d3f63ff8e9d42e558ced36e2e5d4c70c47d9b28528cb14379044613b775464e86f94f62e29058c9869322648535ee9bcf8f0b12b1ea3acc204f519f48096e19da14a408266e0c75a2f25a47aff402f8205a562df0af8e6b8ccedb6f16128fb2d577d884101b679c954fa42f4ac6715205efac01ae7bd1f024c6132107293a1553365471c18c03ed9bbb3efaba7a9cdcf4afd7116da94d4d486381dae6a51c4554f36a25179d759f998fe4827fca97fdafcccb17f7c3b9b29db30c16573e4df5197879a1251d3a20015accb4d22271892b67442848049adca18dcbabff5331e409d702b9f4ce5dc8fc6efd52d0205a4516d9b40c64c134bcc23d7627d66edf1d4cd52ae985c7acc213a6686aa7fb98a4cdf9cf2d51b9e05b1ddd31dfc6697cc75ccd917f08a92c8ca8c5faf475f0a188ede9370adb00419489e89e97aba84aa6c77d951f624f9aa99955d56625553c3e7a14ac2f1a9afa3f34f815507ead2a646c9dd5a23fb9fad905ed087cd01ecafe4c903c22aff103f2a3ca614f98f8542b65547bf55a39abd637ebde2b6e437a98f54fb517ce908abac19a16771f8e4ba0887e1b3597b168235be0d6c60d3317f9f792e60863b4060fca1d569252f592e4be383d4a6e86310499886aba36263aaa661f701666c210f6b18ba1d8923a713b639129aa452867e0a8cdc1ae0018714ac7e5890ce6651f7545b2d02d5786a59352cf669926d36a6e3f754a84edfd343d7f88dd270c86f6a7c997e90366731500e3fb282d8c87e1c14443fe2ec4443c7efcc51f1a7f9bf1ffa1be301c4fc73d6bbf6a01b5340fa3d004fde4da36228cc8018b8e27eacdb17bbdd7264a4afc92d63c6bfe9efde14b06ea85a69d6b3100ad8021fe0775f8e3026bdc4607fb045cb447f79eac7d124c3c42a094f9f606e54b2d35f52f1c9001bb4bd8ababc8b16501d01ad354ecc264cab305e988705243fb7966552bbdaf95b7047e6266edc6b12e49e88ef2ff364122afeae596f9ce91bd35073214211c436997a4a33d6fef5e70337e672d8381a93f5b4ea1d06c51c21cf2096e1c41634d00c90b794087fb215c836e4a64055cbb9d50de11ecfd6a1c3060663b2b2e216d082dce2579c3994ead2afe49907335ddc49d4fa813729246dce975da1681aa5fde9249e2c1c140de81649f13dbb6e19ae3a9276ea9d0a08e1750309b0b5fb997116c1e20f25234b03c264b71c4a071bbe93b6d0a69e96b3b8edcea6018758a2c7c350a89e5428f07ef709b5bbd06df80ae113dbde9e61c9dbc99f9022595848e4f0adc98fb3c5d9e954a6ebb28eea6929974874e43277ed5837eac650cb979f976b878063d0221e6cb4822bbe42daedf1076495c945aa7144ab839c9010f5803e219631b6d3ec25d9b21c36d1c2a68efb4d8988bc82e44666690f1e8141f3fe11884f2331e475ba32ab2828b967bb83b575028e4d557953ee3b31ddc20cfccfd83d09af4f819ec755b026d64700d8fbff93d76356cf1aceaca758b3c0938ec673153913cd1287aa0ddb0f8c55cebcb8b6bd95e693e32697fd45f0d7f1d346b773dbfa16ba8917577a6db93867c5dfe647fc231a750f07426e89c4f3d200eaf6e0111401ed6742c6aad55fa225545fd321c94f5edc7feb241fe50fae82fb8cc5f9868d4c5c036e2cda9da4e93057ad89a8b90102b0c9ea0ac2eb9d5501113836c665cca9810f9dca35f399ab192dcbb0e7519c8e948014873cf2ca61705d8ff4fe49045287dbabf5332675a57e14ba2810f3e537644190b9fe8dd2d3120d5b7f4fa873c1b794cf3eb623d39e3a81d8f20115bc06dc33bb3eb7e353f242815891e9dcc9c791ffc59e065f8644e2de3f017d563a9b890905bebe871ff65059ecbf4d0d89724072e1f589411113ccc8d23ca44a1fb4025fc2e668a46b8ee14499b147e6396c868861d8005d999377cbda38057848aff4f8cfc8e8dede9dc28fb01a73e4dfde3e362611ddaec86726e945e330c5c20a151ea891fe74467b60d80010d0eb3028bfeee573db93dc2df313c89c1e545cee7af941eda5eb8d76b0d7aa7561c776b8398ea77ea3611f65571284075419c9775b05ae1c25cc9c5270c3f7757b225fbd1bbe5a0d8a96f0a5712ddbf0e5b143e1d4792317599f42bad0316fd73d3a81d365df0237c745529a2dcedc0d7b8f02501807d36936d8a54d42f1757d6ddfd411af39155b234e2ca522cd89a0759d0c9b3009c21e43401e27e22b3bcdfac5a4bf8ee81b1a1089eb4a643126ba01e0a5ee69879e50293f1a85317111cc292cbbaec3354d0863b0707b0cf8da5ca077515b36f5f38e87e4fd70f0c4ab1f4726a7cbe45ef9f9c657aabba65cf75f300f8b159ba9445537e997d1c0a38797ccf19233d14cc678b564aaa62ba7d6fe01172087176fad6c022a03b790b771c9fdabe10966cab2c1f84d6306d5cf48bb0347d80648abb1cbe78018d9c23d23b20d882438917282c6f58e00ad0bdeeddcaa14e7d16afb4a43c54369156120df55e8d76925bc74e69051b0921dc7b0d875c31f39c25c4810020ee8e84c415772d10884a64cd58b1124838dc77ea531ff6a4713ff7df0a33ca14a524a1f526e18a39af4a43224fc7a5963aa752dfa3bc5404e3618c253c596d477babc8da2ba0faa41b3133f67892399ee860d6d5e8d9ee061e39abade8d637732d85f21517083241064b003dbc1841770c5d0bcd04fb2503548694c9d97633514ea2edf6105ca39c7d4b0f3d36fd793044be89b2fa706d58efa0bcb573cf9abe4d11f83f0c183013b5811ae5062ca3bc440b364998342c465f1668d4bbfaf12aa6f410392980d6dd86acf51dea00cba982be6df81d2b8b4cdadd98c3bfb9a91a771744250e604c7d24c359ab15538ae8245cda8ca13e343beb9a85573cd96828039985b30d246e7a5d651c10d38d4347fd971cd456cbc9e78092ebafe7df820a530f3a5c287b615013d1c779c150c58c5e31c09fac48cc02f8efab3734f8a832af325784074698dd42f5b194bd08879c34a6df579956489206b6164519d972288334d47611ea33bb49bc107f82c2345721e796f87f53d326e1ac09c4f18fcab1713ab76d0c7144690b06cb2dc939f5211d1d37a8ceca6f6525adf710fe16c845a4fc7257281b842c19810912bbb1181a95664dcf9cdba87933d18a5048fcfae7664ac7931fb028726b00de1f6167b66edf4b3f8c745d12ab05fdaba8ca21ca7af9da2ec00659d049dde97aa49977a67c603f4c2bab7438f0bae36297c1d9aa9e896ccb361a60e0636664c2e48de1a528a045b3a01560b636d654a39e0dbe256b47723048261f4a59ce977e1e501672a0cec140c227e4aa4ede24653dcc45c5e388676a36072c7fb30172fe6f20ba8947997fac2c3696b3ba00a0d5a72ffcd5303b757baa093f434459a7ba88638f93c652f21efecf51ac4bd77fc06754036ced23b37fed22cef64f7cd5bfd1e0dce9c8a5da6d2974277eeb9c58a9dc3b3c6377f306114421d1197fc743dc2702a514ef2005ed4b34827ca67956924d3dcc86b7d9b13d340a2d78116c10b1bf75ddd470444bb558272220216fff391bf80e073927da0f0528c0154b1ead201809b2fbb9e90d21130e0de106672c7ac9b9258ee31b0ed20e1d2a3ad2f14524dc6dbd6519ad7669c43718baeb840b4df0d60b63ef29e6e9dd2a2592a2485b3d08d02c3c8404e44934216e34044a22759b8b3728f223218b5f295780f9cade7602d5ee2f0e082b8496b6ce04ba731f12b75cd0bdf676cf2af817639ccc9cd465e586fafb46aaa741e92e1261e6b717887d3c59b46614c6755012e169cc4747a8ad9b7362db7c3b79961d0866bfbf14d6d6183088e414c4b3f81761d6c503bb919c930b6fd5183919068902f359610f08722729e69e6688f3710f9782a1fcb7c9761bcabdd9797daab86a468f69163c49e59d931f3325cf2313194ef9805a4822a4426e999d895f9be09611877935c4de99732a2e1b995a04bf293c16f50dbe83edf3b65eb4f1ef97f388090f68a459cc0418b59f4aaade9b54115cd4c9cf9c7cb92fe3eaa5773905f71f9d1be656454b9291abb50c854777260b0765017ff0a27eb26860ece0b01d7c55ba4a8c228a82c9c5ca23e13926c5cc1a833344934d57ffd077d9071e17feea84956531aec7e05bb1dab9756ae3e54b2cac7af90c75f6375648c2f8b674bbd9b185e60de61a8a394b2bba945c779e890ca1867869363f0464d34287fa19894f703d397a8d6a458aeca9d7b4e58183d80a8b15dbb1a653c8682eb2f4b5b15a2b902e4a5dd9a7dd0781e66f354b81a10c2c6afc3f1ee3f06e4cd03706ec2acba94d3cb3bf6f922902a175e7e66c7e72d02e8826662e5e9db072f79431dd1512a7a736fa613a54bc5d0f48301bbe05138a07f996452e14dbba87e625c5c707453408d57a90b5e1c00b32d529124643903a1d630414afe6cbb4844f94564047eb9aa90e20e6e6e8e613dde4522a110711ef246c165a05721424b72ad332073bea9cd9fecd0c17399707dcafe6ed9bdc294a037e9338dc66c09b9bbe36985c5b021b32332dd917851e19901e49fe96834d851f697162287487b87c4551915ab4b151622c982acd7b0b9929d96c94229bb39b294f5b3cfb484e00e734c1f1cf554245e8ed206230380af73e79c4db31bfac732de6f61ef04cb748a47dbf487ed550657ff86e49bee29a65b1715a2a7e5d3ab32e1b1433ad80a8dea89f7575572699057956128d7e99718f00c6fa760e017f0d2ab1d11445f18995ddee563f4be7edbc55c00ff911054d40b1a55aadac71d64609546a0bf7e60d23278f4f271af9f104608d828ddf9eb57be964fc18f864767121d36926304da1c1a148a52ed35f5e1a3f6a51829d2e9229d46397235df4a5040cc43d240a26cc55434b407257e196ada5d493a715143f5cdea8530e1d45d1e92c391930f76ad6d19ec4ffe89a0bc977aa9ab20c15ecc1f20258e01a98876cf12347696e548b31dee516049b98cde66dfe19251ba1ff94020dabe324b200f0a7050a238845983e210c9e15e8f3ec7bf2e6f18725e4473eb3dbe7c9d3672183260b6a78bfc93809d22bef74d3565c69fd13b8681d6a11c06d73dff9664c1ebb58254edd9a42b57c50d0e2f7b95fc10c669079664ebd96a6f6b7f4d002a77ed57c04d5df10845ec831a420a1c982975d1a6429b32f76e6317ec8786511386b681e25cb4398b7c549a0c0d9a732eeb5c6764e4f87ac90416fcf22b3af1440b73eeb0bda7a31c39cfd50515ca746a465382099ad154f57486ff330476565c51fdaec116cd06f12135c0e45eb7e09b570a61f018e005b23cf44e6ff57866c9c8b41fc0345299c562c4736f442ae214374c27d57b305127384f6246e3e77da25cc699f1154465412f4241252c9de6a83d1d3d3dcccf0698509119b402e4ebc99549d431000adf755ab7bc2e91eab518f9a869cf90437100a41efea719c022f1112b0732d92aa49a2e0b61579304f87fd5f2efc52e7c44e7833d768b2235c7a2d8f9d2b29f45ae0e6a7e4310d26798a38a7eb97e32fc9dbcfba1f7e21bf9558f9710ac719295105d1f67e56210f5b92470665106668ef157bdec212cae3e9d3ff5db6380510092ba62e722d8e15b63dfc208f0da6da7b7b8c08f1fdb332457579997dbe1a55656fb14af1665618bd99835f2cebd09fa5fc1e2c6e7b1623306fa3cef8222118a484d042c7303a487143b51e3f0362149b02d18386da67b3f2408a8d54600334f79c6ddff8fe36174a01050d23ab2d4824a4a9d2a83a72e9d7a5913341ef209237b71535b7642d53bd9221acf76ea745cbb20214b6329b07d4ac15e330af12ca05b10c4ee3534cd732724babb196b5fb9d10c6dc91e1ca941bce024206b625cf7d37d944300b328be339a3e68f309e9b49208e5b128530b10c57804c7dbb2f53769ebf98ffa07ec1792feca939be56afa2d3c507f3548896b5c544a36b3f001543e006128d8660f2c0ddbc1449c8abb1df3b43d20f1a5025b9d702735f76085ed8438f7206db012caeaed7393e2f38baca305a21adf1627f34baf1c30f080b6dee62259851650721e019d7343938a92790bdd6254529c0f808458a9faeb22fd7647ee7f88ac42223461145a13beacf792f2c1732413522a19cd8912235b20392ab99fdfcbff0730a4f09266760360248046179586ffc6aac16fee4959ed42fcb2a42eae6708f5321b81cd2a6eba65b4e4bda95d492d29eb4cc91a973b94604aaeef27e8d4380fa4348bfad2a5d04ff0c8741a4c6580e70b68e2ba9e109be048200f2f679401340331225721cb55ae25bc76cee9a2e91ec0fbdc7c52121d619e6e8b6cd9b22dfd385235c5fa74641630665df948bbd625b2392b6ddcae6510e57f9b5571e434d164d58add1191e991fefa22ef88ddb9f5489bbd2384d72ca237a23f5763adca7ab06789342a05c3a6ab31726d9af0bfe9fdcd54c3018e8292f74f83314abc69e0c017972849c43a7befc13a798510bcd209b693392e6c55a28ee660e838515626c4e2508b5f957fe493efeabd2fb47a5d78d933e77f1d36a22d4c6b8aab3c7fb36e74d09e8ba6c523b1d700b66ce264c5b282967c63865e52320f520400b50d0ef2a08222620180d77baee87a88f3277f9f07521a15526a40ffe656543e75472c2ed85b40079ea7f9f5a256305c80af89e1bb4f01c8268d02f7ef74c73700ed2635aad1020b702196334d729bdd030e68a4d8f5a34c32ddd246078d8a5730262a24d821ee291398b6acdadee7435f04c5df0c63e76cae163194fce3f1bc643d6b2fb4343c5690da8dd0b5ed100622e81565ecdc4c087dbce2b40d61120db9c0a71d36d0b2fc435f1da021f09df04f8d36d769bb321ed0cf31e43aed2037954298ef427efa3510b0c3c206caf6547ebe2df41480c737356b706e24fcfa855fa8271a3d0c199b3ce7537b1dc19b9e187bf9751bd1d451ef42c088940be2dc8ac5d8593360a22dc754eb06ebf3c18c15976f7a370bd14131f01127253cc2a46aac7bf0060a1ec47d431a6328fa2954d56da83cee9f06e4668ec07be8bb72ad9814f1be714d239172334b189076d76c73a58eaa5184b686ebe8101a8c697fcf172d2fc00e5649329913813eabae5ecefa0adb5a8dc104ab1618c050924147e8813b32d6b2801294e148358c514b2b833f5e3967fc39e887532c43015a15850353650f0a78495124ffaec054c8adc76dd625e75c044f71200e0d713017ad21288895f478722e00ac38d4996d0a8f7fe171a795d19746ec017ebe31650700f4c76eedf63c9da0a8dfdd4b27fa8a699c93d2a448f7d06d9dc52c4930d050ecffce1b87fe2223283f247434ba2ebf193c7ed32333df5fadcde96619c2039c8f6e34b099aca91b8fa88c3f290d6dd09f106142bedf0ac29c1f8c056d22a6ce85f45df8c5ad17640a9f790bdf9bdd55324c366fd72f6d75ba9bfb0d1be288316eda51c89921428ce04183c87bb0a9244fa80f44f03b973351ad38ab3bd24e709d2c7de57471d47ac0df964339fa5a72efc3da90692468046f3f24ea0f705e2c59642e41084ce3b47ee6790d2b6bd4375231bcf180a0fcae273dfa23ade15de9325fa0b39a74136e0cffba7d78cb5e0bbf463b8e089e5e62de7f99e0dd31291daf49a2a338755fc5f0a1ce1c3a9f1002bae3b98cd20aeb5a8de38b42f412e05e532caec3a035c15f9ea3f8db06561ec62ce0c7c0c12422dea24b92134820cfa206a98dd13a1b0fcb0cea8e22e20a3f3c33ac959d9df686c6b551bb4b10211e79312775da1da6a4b2f100bd9ab53fbefaf3ae09315e7a73f7e1ae3e0090b0fa21c0e42a1df058480618724085e343a58da5e825852cabc240c940f14fddc0e99cac75b8d9a5c6758b55d6e4f9bac10532ebc34d243c09d684e8277b99cb32c1a2d59cdfcca7fcee82b75433c2bf0df101951bb97cb6d631528d1497d3777029f3dc8926abb3302b92c0a8f04eb79b16f945dd114b85fd7a944bab027454b1040c01850b5af972a3a996295ef6d617d4fba03a75390f548f465adca35d0362ad013e1cc6760e9d018c26eb2b9f9c0d6a8bb18bcff4407ebe1b1835b17fc822048485770506df4d49cf421671a025f2e3db015310776b35a82fd2f586b21bf2860ed1373a2eb05749addd1898d4bcde45aaa34d4c94dc73a44a9f5266b9e311864709503a3e58518ce96a2c3e8dfc1e447609ee2cf76b6ca203a495c2b0125151c01ce091cd9e331aea74be7bad8fe0138c94b033e12da7c2c19cc8ec55c99091fb9d4006bf121c31dede198a0fdb9aa08420fb9f46eac4c803db032ec7c7318d16a34197350ede6c0343e321d3e860afc6fcd66ced27d0c13142518f054761d884aed3762b43ecd50a59df25c236e708789b8378409951cf05a14ae2f8c301e86e10e9a2c75dac1a808064a70a783f1bd6bcd5d1762e588332d3db71ad77d2c66895f66e4678a867b72d8fa053c51ebea627870628968d693351d2afffd301784ef0093bad30c1ba4ffdbf325c685fd021d468b962ef6e3834b1b16fc1a8026866eb630df467b433bc5030d03e31d1845fab0e2aad4e93678cae3b237e8751e0f33f6eeb7edfeecb02303f28ce4cb8fac0746f818f7b97f80284d2981894116758388812c470d1c522502beb11a2baa91bd7b9259977e6bb71cf538d0ba1f080cb65252c017f7d915cf72e1305cd9768c9cbc233d7a2cbac65601dd702b9b13f84d42fad54b4918a79b6f9b87317a1c84d61bdc347d2b2a707baa44e4727e00fd30dc95e3ffb7627e96bc745a782d686a6590d708e6b424ad1c38d32d78030685bf5d3407264c14f0f86a8db1bfd48a0ef5cc30565ec4af80f653686ebae92ccdc6a6e56fb0ecb116fe6bdb5e9b5df8def29e4d13ba2b4e1c43df8ac1ea591f708744d587ec77aa0f74434a6c0775234b42d67a303f914b2488df06d3adfb21dde903a21536b62058f8fa4e09577da8dced3d6a86f8c9493b47afb9e521bc2e0377854af2ae69bb1af04d10a8faabdeb54259dcbda8e554725b9f33a07c432a3e24bb51b780207643253c9de986abfbae1d89d751584a90996100e543d1482ce8cf358632f600945281f97aa741f687462fa3a23c1c01cc686030b029ce280ca0d82cea2b1c47193e344a6e4da2f3a256652bc22ed410f68ce3dff1894f7a1ffdbef96a47fa72bd8f73a38a915c2b099e0b4ece88a14bc60433cb8e405b050b234ee3a351f3635b76310c77f8886d3281c1b7a50f3626a2cc0ea25994d76d00d28261d65c0843cdd25b5f51f0f4e5c522c4aa467a5776b22d942e3e1de4601229057f6cbc81db25945b80c9c7762a9417c5e27965c09a3721087a8831cbe90fb7d1586f76ea5e296da6406828b0fb40b3322182ac14691b31ec674b650f030ed585df07aca077101dbf75aeb7d8832888831d9362571d9ddd452025455cf92707127b8374b4f589739517b47f44ed47c06f09bfa08e7d71e37cbbb95c9648300cbe5dc0454b493fc02b0e2babf80ac9ae3db7494f0cf7dc17e1e8ad12586001f75c172eac0f81ab8c4c2a69694dc9ca9442ee08444baf13bc42d3a8a9f1ed258b34cec704010c9cf981c7788d281f40d8992b1a2fd99509346da4e18068df280916a9553dd08cf6afac441b574aa35b74fc70521feb1044e4ff0651536c0f081ef95bf341fff600916e297ca6f383dbab078e848ac0dfdcf5a8857477995105d766a9d21236c4b06ba90d461862696361346329ca90b3a5e4fb045e6e0853277376295c715272fe5a774c7282a90b75068993786cd6fb6f11f671f10502ad01876b03a95d256fbe9d0b2ca9458e061b115c572c4beb67cf15bd0db54a1b9f2998435d14c36ed021240accc567f42705d1d96841da9e4aa166b30be259771a1eaea35bdac859a94e677251349fae7455a5df632c235b0d25e53e019da6ae7100171dab09430d4db4986c2716a8626194be4b67df0d4070e6a007acde0809f9012832959dad3f231e74c2dd4d3197830da3a0e2c48073e5cca2d41b5af904dd19078c02e4f2b4a2d0ab7f080b92fb061fd1cda443d33e9761d04b49b976141015c3d2e7c4e94871a724b8f24df4d4ab2688b3a0615bce95bcc6e919b1919ff3", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba010000000000000000000000000000000061b1dc39c65e8463d7d6306805e87a8b000000000000000000000000000000007a88ccacb47519f313bb4df7a826f03601cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000203219b289bf708a79fb51b2ca033a711b50507c0a223589ab5e58ca23d94046612cdec838f7f61ac1a22b03689875a641846bd79456adbb7461037044d0f88ca2218aba4df02ce9e79dc47d8caacecb6ace5864e51f2b43f071bd722a7e2728219d950512dcce7ada3f9d302cfa36d3768e83ceee44bf4287f509a003a7a4db70000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x0000000000000000000000000000000000000000000000040ab06fe21da21825000000000000000000000000000000000000000000000003d09f6ba158cb416a00000000000000000000000000000000000000000000000653ab0fe8456da7cb0000000000000000000000000000000000000000000000000000b0d51ec6f54400000000000000000000000000000000000000000000000971f9a84520aa31270000000000000000000000000000000000000000000000069a8682bd694d08990000000000000000000000000000000000000000000000004aac14ee1f1f9cc300000000000000000000000000000000000000000000000000023c403e139d5c000000000000000000000000000000000000000000000005840763058f90f862000000000000000000000000000000000000000000000001af162e6dd3d19c42000000000000000000000000000000000000000000000004606b05f7c092197c0000000000000000000000000000000000000000000000000002ec3674acffb5000000000000000000000000000000000000000000000000d53670d6246e537900000000000000000000000000000000000000000000000f962e8b1c78199d1300000000000000000000000000000000000000000000000a30b52d4914d59ed9000000000000000000000000000000000000000000000000000115258cf5acef2743e93d4e6374c24aef3bebcc2f4f2420e9ba7a5714ca96df52ec420a5812cc18aa6c4a565a8eae50ecf4b59ac47f274431bbfabc786534686220220083c6d426a4521a0e9e50e463baf8bba7d862eb979fee57112d10d6739cfbc5a6fd1f9512071ec987928cbd4fbaf9d86e277597b28f7f28b44afac1d78d050684ee7ab32ef47cc61e6570347351e7d14d514d6aaaa183d8f3136db330f81539a5ff03c021df9c271991b7567f82707a8ad58cc0f08a854d6c66f37ac04f4975ac8b16fa235b7da200d49e8945b4239de3bbf5257002bec0d02ca95619441e75985ca786043564090305898166319bc14b2ae8de7f23278e47910d4af9b22c1af6e4d6772bfd7385b9d88b321d75fb535fb79e9653cec5db75ad0ee5b430d3cf69f3f2e42a8c8cc6a439e517bcd51609fddca9af121f15696115028a34f01b99431f8d2d084c40dd7a68e2440be69d3c4cb4193013c9a52d743256ce32f3ae7ddb9966c82d4e413c9b46708028b32614768bf0ee52c354d61dd7ed7a6c07b4b2160877f005217f14563fcde0b9affa023d3ffc28acdf88fb52a0ab455edc3f200c41d78419495bf2c53688d2c38eb25f9b069fe0fac1156ab227d167346d68483944b5cd02d388139006df14da7dc59c66306bb63b141ffb44b153d3a2ce2be113a26fbd0f39043bbfa987f698498492d2dd5a9a107e44e8c797477789319d1c368f17d12e2ce36c2407c29941b527945fd15b8c65295e960b553088196c5c3efe8909610ac2ed3cfbfb0d8f6b42a55b7d96c1d6b876bb7eeaae3a52cb522e43842b6cf719af9dcd59ec0090750f2b640dfa3838c30b7fc60d524f21ec41e020f83136ab097d308477adc85664b412502e9e074c8ce86605f0bec32cdc0a92007bfacb4a0724afa887cb10b384a0f8a2f435e5a6a8a2476c1a43e794b5d040de6e17d96705a97dcc7110d434ef35baec043f75f16a8ad2a65ef85cbe11992a9cbb31614b1ff0f0f3ea28e6d81f4eb1d6f1370ba58e2534cb627378b8b69f6daacaace2fd158335faad367b8a5f88eacd5165afe29486713ca7d4d0a797d1b4a559d334dd13639843bb7819198343f00f242d17f223126fd1719b0817cf7fd5687b15fbd2031d179543cc51568d8451eb313243d02cab952c0817f78098276b027c46037f05df41d2b0c7bfb60da2364a6be4d4c6efc8a3bc3cfb91bb7800175e8a7049322469195f5e50994463f2ebd5690e6dd07daa9e797b5ef4cab2d13985f03f8994232deb337dd366766174a6024864c57858399fb5aa60ae7ffe0bc4f60558e5fc029d082fd155f55b3b7a1d5d11a9cc8d05db1bf23bf81781e0c95a310768386f136393e0465b8d7a920b5f46d99cfd947040ebf2edc4c9b56515fc197e7edb320d5ec04cff4e878dfda85a4d833aa29c3732cd973cb57044f72f28e220f086fb2394986b66cb4c5448a2e31fa30250ff6a017e4972a2178aaab76d92d14b59841bf47a8f314c8ed5cc73529ec625ee72879c6c70572cdfaa59a411146f53483803b645f74ae55e77770167dc9f8b9f88352d5d1405f2b2e1793f402ad14e0ea93040f6b5948c98694398711f39e7c5083d3a864d51f5d18ba87dc2604e5328931206b74e1af900f25a6159e24249b86e36c64edf56148d23e3e38eb6c415256d073fb0322559c292ab8be3d1bae23947cf6859296a20c33125166ea1e5db893200c783848aafa0b2076135f23aed7b2f6841c5d864e9ab0319b223b3df0dbff31c4ec54ef7cdce4533e6d6646188c6e79e0c62271353827e0cb821a320bedadf09e16bbcaeb45335a89d7dd4053c17ba82a9e0240b3fff9a7cd58af3f70f608d2e354669ea4f1ad977ad4ad13ababa66c1fc2794ee78c9fc4faaf890c9f2328018cf65cd2daff5027404b5baeed49e757db29c96fa3219ebae1a4a1c682c85131133f4d09ce60ca38a9cecc6a345a442cd11b6ed794e3bababd71f35c718c3b21a461b3893f154353b00e3e5c85832e6737f7100a9a5e29e421d31ff3fd1acfc177698f7e90ec5658b4e542ba9d4e202e46b6c576a1d694d7dc59c02236d052a098e5044c0f357415a36fce6e34465177fc0b914d8004ebb1ea7d7cfde7e91bd08cd0f8c745174106bea30b2acb002d58b77a984a681747c096470a038878b2619747a68514f726714f01143ba33c6067623d8d784e916716ff2da9c40f34787207b327f55b17bee72826ac64f91c43badf86e8117648079adc97e81059a5ed1283696ed822c0e5b0728ba6156a0636ad29ef805fd968b0efe04e72d174bc4dc05d38ab82149ca15c737f244b7c198deef75536eb116a6b70c99caf5a411978829028ab6b848af1a7bdf7240b46e78dfaa08f6e20a2ad9d72aefdb9b657184162d1ec631077462701554b4917f1598235d2fd2e728a6296e841f5109749dd4ac20add72426e1dd53e17d74de24f677e889c8dc52e462e9fb12989153424b619a168ef2fcbf4e23612d222a24d236a685e7ac1dee99e55a15ad5c5ccd1a90868c1f16d13b8af94831b9d998517f478b12843e79b2cf8b510e731df1e4e53d85f027ba9b775ee38e4722c1c50d840f864ad79d5b7f211bba5ace55624d9d67bf272750e7e77b265292e81e72b769ac39c69d6b87e0abf8a12905291bab45049bc9003580454d1f45cf3209f216a215f94e8fa0296039d960ca361184adaf05a5110ad6e799d393a4ec57ac1b496f0bd4c2d2d0a21704d2e807c02be4c0e65f71861ad74d91e1daae0ea9f7da1d529adb5387d14c79e9c88b2b0a5ea1354a2c451726d5453a94e0188988fcd6be18f9e0daaf1b3b4a4942c4ffe4bbe72a071ad2500f4019c931b7a61459490286f70ab69c358111ccb1062c984dca75e9be31d83f25a3f36ec88d2dfdba029caf2b502ac2d27ae6e2e4bc2c51310b577be3097b1904113b78cf1b719ea164112ef6f06d8203f3fa089bffa1864bfde3cb53dbc70d05f2887b68f9ab5de5336385f3970ac6601ca216e60bde4fb2727359a07121df2d366ccdfab360ef1f550cc116b3c4c12260c44d115662e16480f4653e71c601301100a4d931b185a9f7237ed93ffe377fb2c8a41ed83628be99e96edcf0ee871fc497dd70ba7658fb904f63552ce36737823d94cb2c86b5d043d00771798b0e137c6b475a99e2fa444f6249c5faded17fd2593e841741993be5e10a0ceb3fab13491b3cf515f2488e1ae936799cf3171105220015ed05efd0df77b02b685b850d4dc8e08ab1a1e4d84a89d55c2009f137b071b5ef47b817e8cfcfdfc318f7ee2e574cdd771653fd4ff74f23315ebec3e844c7fe91103cef4293ec6814eef68628463a2acefb7f863024e93710531118059348f66c509d87a173c331a959375d2d484ab1e6ff5834122e05bbbb8c5bcb5a3cc4f940e46e230bd2eecad42884042ae070003fdc0787eda93dbea86db1bc786f91b8124e8bcc48b53b91ad2743f316cdfb4f8af4e985375743c1ce93ddcf33243d2a785af13054b6f0a8e2d7bdce2c0f57dc1c3c616fa0cb441100f067dfdad8a704b9409c752b6a44a8192af35104d6a676ac93f8ab16780b7311c4d9e09ee52618cf47f75ddb4debfa560e325f13720280111945493a97974e573a977c4be5cd28073fc26df9e47a030a7b3cd60a9d160bbdbcf86a754657011e6221e8d3c0df4cc4f51233d4a587362f34660c209d0ce770afb06493a48ff0188e7b0f2bfdc3290970cfc07ab046a894581ca208af11ceaab5973914f6936e5e1bb5c7171e41a83758ddace922067cbda7bec528a70f53d8652e346f2811637a2bc348fa4a329543ee215284507e18213680c01b87d158aa750a942593d21665b070248eb05d9d99a3e718e83d051b80b4d0572cd3caacf4b04d9b02e7b5d119f825c51ff746f1b90690fcfff820812513341a1e518c70665d56c4fdd8026696ce962b244efd18337beb6d851371691ae548b0134e21cdd57a574975f136c4c9df4fd47242e4e8d5b4d4d5c64d434b7dc224f80ece578109ba28c79b93d58ca355122180703b48e1e65bffa0e4b8cd0834892a1af3698b86a6f19f95d7bb233b3da96574a99ae4be95be5c6a1ac80cecbaf4100cad09b438114a5de5e688f2350b6bb9628a8d529f64a0d03495a940b8af841219cbf4d848305edba7019532ed0abd817e4351dbf89985edd837bb84a253897f0f45cd9cb9704c95846fca3cbe168336a7033b9e5484e1ffae1377e5bc24ac8604d341ff988a69336a5227edf5f1ebfe4c03b8080006f1734bcedbd13c88998b04fd88933744df41cea43cf4364514c189bccdae52b745765d39cc7323e0808926e73ff470ae40e1274dfd6e4f1a1e43d34ba15f6cf124dd408631b349b3288d1bde5a64444aa6d4d969b48fb876fdab78c00be233cc6d7c2285b5d57e0d541a215591b9ba63f7a1031540a0fd2ef4b25c45401435283b8f5714a6593377fef92883a753b19056b11835d43a8dab798ae41ae19bddcc9b77e4a7d62a9044237d25debfe3113d1c32ceef3f28f255fcd7d7c1cc926ef05eb8fd38452db84907580238852d6123effb342ca4edc2e2e6cb1f3503ce645286483a24fa0d5a5203cc1901f6bf1750b0562f89622e7844fe4dc4df62f393e2df9ffc367d5559fa0eaa2e9d24c26cd530248c3612b7b92324fd60e9f2809a7714a432795348dc35e1b6157cf914b2e55748cff5db38d153c4bbfbbea5d69640d6ee322b2486d2ed27b206d734392617d64c10f7e920f8b861f2e2ff047f95f64234ab271cae6257fe270956d1443a7ffa4ed7934245c40547d4f1849536d1a325f89ab9c7068a918795136bc9fc6e672be03de71c3480bbbd5d86b9bf3271aea5ac41af91b48c52898c165691c3be42b9f43326f98e9240862a025bc5635dc1c606fc0f2509d1efef1b0fc08de20191f99e02b70875d3dab5664655fdd5290c12d141284f03b5760975043a240b864666ca80bd0d9834ba077440690d7e32958bb09128d192c4a882d80291a7bdc10b03ac9e1ba37e18351704ce1a60bc4cbdfe5396fe6973993ba8a0217669bd2f4ff5f684fdf64f00bd579194d190e52d3404eb217b7bdb07635a8704e5e9cc2eedfb4d5eac2c8c97f99a67a70b722e722545ae14a382fe970a42ce2fa136aa4acc0f44847caaec9e9b5894fce40b03f53e879fce904b96b2944c3219a345407d3b0b4701deee680e0407a6ba609db5af0239234e3038e2abe9573d1cddb19fa9e9f7cf586724b8ed082e215fdd0da169f6bfdee0c722e449c65f262b2aec80e8ee480b6410af0902bad7c53ee032e7835fe9dd4d3be4567195df882e777b9560db7442ad719eaac80f50ef874f2c54e279f3d54624ee02f47fa9501e264fe1f239017e8a52e0b02360126fcd71fcf44f9aef62d0eb47d6a2ae41e72ccdee6ac3d32aea7fd40afc96b019f5a9951a0b411e648fd40a41c1da06214401b6bd9f4b96db40cd2e3c3999220a789265f0d09426777713470380bc5bfc5e1fbbed783e3fec98db96fbd0ba69cd290c27907ef7b1982e7ef331bad447a1880722f5b7cb793fd8f27b9ed1d31dcae36e2338a2131a928c1d0e273db270b05e2f9b8b2e7e2762b41e67be8ce9aff152d20108397f3b73bcffa359b7f1ef0d4a2e963a4898c66d5706967babac451f8be3c3576e914f059af6ec9aa0952e5753019bd4cf18e64290f1c165856c17f0ba9651543a42a2ba95777c40f383d5d5311e747e15b6bbc7b2270975e0fa26e5a46291cc68074be35e4aff55b3e5ab75ea0b86348e389c8c5fd94f675becb39304de9b7aba1f5f24f9436542ac7270e91809379260ca1721fe623ed93c31cfc73290184e3fd5be777ff9748a62635aafce2400b92b1237ecf7292a17c5ac3b3d49ce2c996f9be7c74022ae9328269095f4261d23c6956f89f4d53b4dcab088be3fd495b11761c7738db37914e27d4ae9a02f40c24808c10c618690498fca7f59fb1d4ad4900940178b7cfd3c73d8a0b4a6299532a13a9c4aafe6663eedc2abb30f5dc5efd034730725391abd53c9b10d0c2e83c607da7e98c97acb9a1f579133f4cab156b61d5db4d14bdbeff7f42c65d419deb1cf3760699ead370c8b82510c0b32c579ed97d96a225c1ab0686fc6130a074eca7416092782b3271155c927f47020e8c0f1323f683cca2ed574050f733001814f79d7e5adab32768514f5804dad018d2ffc2eb9339ce639e4217b7cbd59290c6c55c4161c0fc95458c7b2caab39bd0ae622b1cd31a6facb5771fa91fdc61b8350d295d4a8ac29612ee0c7c58393f0fce2304cdd04e26b903b989a890d2512f3245c84b74f70e541548f1b43103ff12d4183ca15f6c5c4953149159f688a066190fb34a540cf37b4b011e5d56d702be708ac52a6381e6c7c14fd5e2390bf0f02054dd47b7c3130ba3b05a8358213791d14abd0942f197ff41acfd49e25000029febbf849756d5b0a1ea245c8e72a8ff5d8142e0dfbcb03d081a1e6aa523c0b9be309992df10251df3500bbefde5e398900b31a2873400259434818825128009b76de86ffbe9a22f6b0401cf2a1d31b9bc7a467edc49d08bad4a3be6a867e0369d0df4228f24157e3e74bbf98cc4fccb350cc0b1453985f35bd6d59eb3bbb0c8a708fab3e36564c0fbcf59b52b07d420c6c27b21dcbb404d78ea17ab65ab22a7ee682d495e473c0b1901da9986e35a66d58b90181b66c648ae8a057a0c65509d48727dae811979c6a30df9bb90dbb3835baf0439724bd6a16546f888067321026e81944747f1c22e9ae204cd1991984a23fcfd5310572b72d74248c91be8f1d39a8b44143e8e86c599d26faad8bdf1aaa2dfe0a5a6bbd420fb8cc4827abf30fe2a9d0839b22588fd2a0dc6cfb92fd6f86afd7f9058f6910f99e65a315a0c106958952ea6ca6135d6b09f6e67571bf4a4e4641f96965dd1706c572337152531aa504225fd8ef9d54705af04553d536d3dcfee38b3de834864252be74304ea8088fafea00475b0ad5d40f8ceab5961c9d626cafc84b26f84400357447687096293abc23a2e828c5c37c97c38f866911d9ca984a03bfa86b291bda99435500732ecc4388049288b6b777af9d4758126f2a552bcc16e75bf97bf596c0c497237f1c9f0b1ae3d97bc4531060d3ac576bc01cd9d5f10e137030e4bd33a81e16a8221e79ae12a6f7971c03de925cc87490f9b64ee9db4168f1b03d18f286ee8efbed28f9b5457b522f7446cdf3390c66ff86bc466884af706dec76517f9be11b1c4a24ad702d86399352c2f7ad293ae42d8eed275aa8ffb377b3c4f2d547deb234e62763ff0f3e58cbfdc39452771c14cbc6c8eb0e6911d53ceb304a20947766d8b415fdcd1099e3d5ae5021bc943de2cdc11ab57fa9b5a6f6b063ce886aa3eafc5d1a518044009fd94014486fc043c811bf0ef250d0ce1a073653c935db8219087c2840edcf89f08a32eef607cab2cb9140e18cc0cdcffcb023e0b26ac6691e290e01def66e7b0a31bb1110291cb92cbecbb0a5641fb9f175671ed9a114b757acc6169bebf9c9bef1c27372d5509192f4dbc0999b57d6501f2f4e04bf5be97bfb33138ccf7f178db34be5d750cd2bfd8a2375ee0ac34b4410896ad8875efb8d81131f2d5c591e39c8adab09adb775f559b695f390e934af08c8182757cf340407450bf3ab02aee67a410cff649e08fb935e1f6ef6556706d6f02464125ca1c6eb8a2db6877fb4822ab722f3b8e9bf03be6078fd699fb51125971b53a39616de119605075a795d83199b15c0d103f0c91f11395a517d1c83340c2c91aa42663d1a3e071e4414d800496e2ee7d8551acb3426677077be3c35d7f41c69d4bba4606ef7036ae83ef1b1c06905b50b2fa351b94d4be937016f6d8533368481481ca6fda30304474257de875ef5168fc4565468471c81b4c65719d4037d7cc7c835ccd80c01de4d6a992f902e74fe1b3195f1ddb53a672b331133175443811b957c1f68090dc846cc9b717234f1aae65a03dc471461efcf21e69d8be21937dca5aca848c90b98782989e7ad92e85b4f3ae502fed8f71ae2286d1336ac0d32b8a33de55273139cc206d8ffdbfba5df914eba7251435de570b239374f98276811d3469e45771db82c1754c29ac6c6e39643b34feda8dc704facf77b1eff5ef2fb8e7aa71def11a5401416d16223009f6de585be285013d644bb03ee8d9e124ff9ecd139914a2d37d47a97ef260781595c5a33bc089bf5b870b31c2cbb6e2cb96551d08fe1f100f2311a43fcca3ae43aff407034fb1b3ebd7b2a26127be42820fa97cfdb8fa02f41c7e12202331d288ff905e07293cde5fb1b4416853bb5c52bcc2120a445072554185db5bbc6c13c023e10e818333b3ea63907bdcf0275688f4480a8727b2f183dbf9242fb2dbd3320d2196863f6fb29fda90d4d090bb3afba4a59b2c68a5516cde31823fcc0d15ffd5476a12e54eb6c38b28d04a9fc2bb41e13ad2f1f2875138bed8a04299e7c714f7413e5f053e37e304248e7d969a4f994d15410f15c2b1d2aa31f2d70042e1d242c15c593a7a846a95fc96ba029425aea988212bf900e0e9b2a97f5f8b3549fed8148d271db03cf878b601a6e694024aefcc8ed72093f03a135b5fc9b72df85fab4825c6352711b7a71fc9876d66ffa5949500a6508c52518966b85d42ab90240e68488817e62085f222850ee38137f65ef5173eb9d341f47eba397a1ba71a4509aaa73f535b5e2a771a99a080ac81c2632a3096b287623a2779126dafcc6577a643972a6922fd3eb24bd3281dfb6dc1d19cd224a269f013a2861c8b8fc8036d89c1689e27a68ff32162685f45ee516f55279ed6b32372a3ca326977ec6b34cfb295fbc099ed807c2284bf81a78546681214ab14c4edf113efb51ddbbb7189ce15790f96f54615db145c0ce9e5e818a59e0ca7130a6921b9ae2514327086240dbee82447f852fbd5e599af80db39784af850b88d9060026e9833fa6c872b2c1372f93099606837851a1a168c48145d9951640a97b72092042dee789eaeec2724b826000d39dbf75ba4710212bfa31068bec37ddc16a64050095288cf1d25c9f9489012d3349074eec75085803491ebd8298924143273d1c498547e97386e641295ec8431522d17cb9dfbd2f2b4217cd83c808c7c5f18d10737bcd0f8950e8b2555b78e3f78e91834a37af5386fdae725e3f20bb54030e189b7db9b6e8ec5eff63867cba626e652be6c46d665db454445f0f83f08084af2113e5da93928627ef87936d16a7000a0a094a5002c5735caed7f84e96fc8af1114de25f98cadbda11d3a91d4d182424c842fddb7c69cf66ddf1ecf33311c9e91b198867975fea5bdccc98d7e30e8f3c8d8a12e2cf1da8a91da8373491c658ca0ac6e1bca3d1a63e41cbcb27336f3072f08b53d48c7ab0d0e077e2aaaf1ecc0e1c3898c8633a490bbed7d87f0b648dc8f0f18c049f79f8510814b0513f9716542796fad753e5425f502639f66f85280d9a026609045e5ce3d85d58cddfa0760f2da4146c8424d471d5bd5992a8fbc67b3bc24a107b83575ea4ec667c8684d4a50d879310cf292b4176fd2da386d2827c6a949c75f45c1af3841c6742811f4e532b67908e68172683560e3ba9deff96afa53543c7525ab1e2572c675585600a3c0e8d9cccbdf1d2dba272332cfbf5dcdbf4598ca883d526f0348b407a0b53b7f72d63519b8a3fd6b14bf1bbe4d0caea0eb71f6303aeaf5198f0b96c540561c7b024593b5c3bc10bdef4eeb418bd7d40e811b46a10d85eb1d03201b1174418ebef2f8d48c74be847aaad4d92bb1c1a4905fb0fad9118f778ded6e9f18e291e974118d6dfb276396599d87ac39e61fbbd9acd173234149be49b533d3329a6a471a00d06013725130cab4546ae6063008eb76f952a8f7b1d4bc364948346145a1d512950eb1ba9ddff18f9a5a6b46dd0375ce99fa87e59b41ca52fa1956a5f5b54851cada72169d4dce1449ab2efb0c6b9ac9e152462a4b94938d0e02d656b2b302516047fd71cfbc6fc5f71c30e004e4e77fab46d560bb462bdb52508506099fbda2a132413027e013ca6de54dde3497cd6f4e165855c4daf55a23da099c2ab11422b7037249598689857c3181330d7c7c0c85723bebf2fd38ab79ff15ab71c736e070cce892a0e995f3eaeeafa42934a50cd1c3f68cf168c50c2541e7722e08b5a1952dc2def02fc665ba4ea42f9820ec05e6f622ac1445b710685b911d67280240ab866edeab9a93f511c74e06d9409f43ea822de953e04b405e4e258f3cb641e1d51e5848cf19ba0090c881d58acc9d09c8122626a807299cd251dea77bfdc6c1db5a608d1d7f90441ac3da7a850b39d37ba055d46822b7f4a440b369ee3dbfe0e2e4afb5a995b21b025423a3421ea55f22a69812efe31e835d00ffbaa26115c1e6fcc35e3bc1ecaa89b717d2acd42f0ace83fb1e88c08230658c64331a0d3c002205b52ce6a9a3afcfdb7fd07f0ff917b0682a827e6be0e519de443bd7855c028ec2309d0e2f5f228b984c5874b5c1ad10d7bd97a10bac7b9828e46ed4b5a281205f36b20d02c74c43cb5a415a3785ca8bfb4d3d4d2ec5816901120511ac4f82ddf777d020409db617ffc7d064710afe1af7171ab23eb3a2827242a81ac15a00c068ef21b34310cc14da591ad18e6e044f3eaace0ee07bd05ecbf722a7b6c292bc6f2d578c6d7d9c998f997dfc03a913dde3e1d965b17f32593a5a0627e0c092c10fda42bc30f3ce904e4c26eff37953802d60afce5cbb470bb6d15412e268f3023a4042bc161e38fd8e621f9d459f3effe4110dc22da5fa1868bd3918cf7930d18599d713f0b6c205283389b0f2453340975f771defb2b5e31c3857dc90fd92daf36046a3cb64afcd9050f4bbb9fa89428624a066a44dc8693c560b09ba22b0a418603118fbf9d862084533831534afd2aaec3cb75aaaf22b396d7188bfb0e0b3709fb4ce0a08b7dd002572a13ec48a5a480dbaee3570d1fce629dc5d0e56b21cffbd42d18cd9b62dd78c23ffc8d2f5ae1c467b80afb45c687c9dc954e8f321b43ddb100a18f3af3880a51969626dc3e15ba3bc4980e171184ee3bd89dee052639becb1659d8c12e42e0652b6a33d148999ce6153d4f4e7afc0e640bb231ca2a59dd79ce91efcc51caf5495bf45e00df2beb4120e7d30d2f5a0446206fb6e3196ba3c835908bd1a2bc0754ab6bc4819f2f5210fa494f76792070e5e2c454be02b1da704e87aa8489ddb9340f8841d94446eb311f21bd78713ae2498aaae1631834d8fede4f068b2a340d75f9e37b796a0d014d265179ae4f24c0e79811981d2ef1b18f66869e914aef1780d5bd351e58eee8423b18cc9314079f6aa2f344601c8b4a05c7631d77da06ced0416667ba392875d9af73a3157ef63673783dc13826a144f350975fa8223473bce2d7337890bd3cf3e56eccdaa2928923b06eb9f5178761d6ee4c5c09d2c2970c06cff0a9baab2d0718f5db3f61455817fe60a7862f8573382c9e2d1eb09bdd03bb2af0db6344c045fb86e12579a8205b460b1c0312cf88883ca725d6b7319535c10442a31afbb47781e7b486a30e4c35a016770c23790213fc854527c7dba54a75a9de6b88d741433c32774d547ef5e9d86d1d76125a9c41bfe6c1c5414b4954c5ddd09c8830eb22edfa6888f76e6f227358ce321c8233821ebcf0fd330a038028d95c8555be7bfe7e30a6e9a95934ca77461df027d3e0d2c5716339668a42412bbbc5e10dd51fe22a7ab706d88361503c99d4ef0b15a392a838b5c3b67c251972cd268b60c15d3fbbd6bebd225ee236d219d942076f9d9bbfb77981cbce154b5b9b55a15e74416b9af9b1dccc957bafc744c124277d650e13f2839ee6faa36b0a3b4db8597de30e4b95efba2d6aae287df1c67b1eac34ac25df0fa2360d47b727bf22d4cff0745872825394be099fe22b57a12614ed7350f7e16da6c81c07dfdec5c33efe7b6486e06b96a4659ec6fd8671f6a82c55903d7c139842b424d91666571c68928cea428bc1d612d6e7f44bf7c27d0f17ed583db00056b3382cf91233026547ebe5f93f1aad6309cfc2e419a7c179520e99025f7f20d9951f7b11b0319944d7d18c1636bcfea5f034be45a58632372204770cb1ac257d09f1a6a07837916260885ea279463d727b331b264b1f0b49381f7f2e77f8cc6a69171a6a7ad635e415ca81bf32489b92644e873a4fc170ffe1117c1dfb8f1d10c543a1d9047564d769469816930e3f41854864298374980cde254352efbe0abd397db0fd78222d71595961d9981c996d5ef96383dbbb766b840876fa3e4dbfee7486a0199e40a93774fcc202ca7e094b1bb99bb5c03d5128a626564e425bac7f3eac2991821756e078e406f16d5fa573f59897d0d345244d590b3d34d64df3afc81bacfd30b9e8164ed6e043ec5250231e37f9a6a8f652bd7f0fbaf17b908d410d6d947d7fca1d3b51ace99f755928d9aeea46034d972a296d25beb92b01ebe662acc722dc0eb9b8ad40227e67acc989bcc820773e1c83aa612905aefce55491f1925af1c04f0f231be3514324a179e5315a1bb37dae95a5661f956272bb735d7b95e89c33e7c01be2b7e83582ae1a355e9df4ecc315e84dc62ab2c1206457dc7ce8ec99f837075700053651b11109c8627bbc53705b593bd91c3da4df434d327d486cd25a916b8607d33f31ec6b9e9bff15eed344c0fe88280d877a5b4bcdb00799f7837455cd919fddd991bfdd02dda98cfc21db8b9b880e0fe664cd7591257dcc7cc3e0c131721b88d6fc684e93073ba5d5b050eba773ee1f1917ab154ba311a46e1de3a8818934b0984ceb63d6f8a0e8fdd1e88f8ccb8521fe6811cbc580e23c1ffea1b329d7e51865a34d1c0fb717e870a221cdf9390a1815630d082b4327aaaa31d4e2c343fe24db2ee45cfb6209e7b3f052f65ea94a05769a31ac312b65e8b702576524d2e51da83c3511a1b211e67cf750a2e2a63a003f5e68ab24af990d658fa911e9b16d48e2dd4c39a5420208eab00611a2952c14a3adf3a46fd71d7dff35d4a329ae6edf88bf82ce63767a264c0f55f58152e12f9215f4f60bcf23a87a052d4ff98d4ea15397227ef1a31e27630c2598afeb781697c705ff91abd6ef567672b21af372848a5b907b137380b6f35578fa10c0442f8e8a9c1185d5390101051a7bcb9a9cc26e54dfc06dce51a827d771e712c4e726a462eb20b0b67b110f61c511ac1d1768c17bb76bd94f4687e926e91d8d326a169341d5a6c63d0f4d2a4657014e2d32ef9e2605e83ffff451efd4e44c31e4331cd7e76c07fe587393bd2896c20f496a1284126981e6dd39c9a93489dea6aad722589bcf9152050f6fc89e1c9bdbd1cd00b14c92dac4b0888dce9deb64d34b6e2799104440d4688bdbdf16aa80a0032eae6ecf4f5457dc7ec502f9d6541e57f11d0a33df49cb75b224d68297662a24fcad6c6c4a1d058b66b52e5194be351854214297bd50145c5117c2046294d80ab362f32cefafe752aebb35e18b6343202b088a5e3740209e74a2dfe097771d91abbf91821bd6d8d2775370bfe80c3b05a20b28e7e4a875a7f7fcfb34543326cca4cf25a9711579795fe1cc240533bb43bc25531a47002615f2a582ae9b97af5ab449ee49ebf1524fc70284c5db9d9ab8d60cd6a756b5b14120ec32650a922f09b9ff0e05191d9cb64e8a49bc9ba9ff38862f8438c9f72d8de501a998b72b3e094cee71964c093774e6cde95231bc7488af29ef82fc95f20b73e22cf7397dc2f815c98cc0ab35ce3aa2573b3bfa52c1c4e20b14ce9085edd1ba3efd5b4e7eae79db51e0c0b6eccc2d8c2fe017b1e953050b10865d5f69252f27099e8607385efc6b34356105e0d55711c256623bf827cd6601ebdd0093ccf8b5b7c22a4a7c023a8f3f6f9c2e033af6e983488fe17ef544b70afbb9a6c7baf14c4c66d617464ae680959258f3d6d0921d8220bfcdf54a201307980f3e5d8997783a5cc6260d02bef3906564b64e944d925cb366b68aeae3762bfb562400014dcb231e337e4cb1835d935a6ceef1cb72c2bf7ac05b9b7063b82b65f37d1d1f7836b8339d8b2588c630d786c8a0cf090161b1d37814363803581fa5ce82abb747da6d6c34a0260e627bebd1d4776e9e328fafb6d1383fb9b2062ee54c659b246a40a81b5eb1901216be06a9a6da8aa2fcba770d5ce232f8bb551259bb0dcab25477febc5d2db3124082c3fa025144e7ff3a2f56d6ba87a7d2df10fe1067d51a1356857e5a675b99f2573747dc685678c1896d6d820d5fbb6a142365320332eba438ef44aa656caa7815f18b3f9c59eb23e8b60deff463beccff1f48996cee11a6a538526d034a9b60f065fe0f5039bf073640b2ec41db6fbac5193ea88028976c924952c5225f55aa1affac5e7586245ba59dd8a2df0bc13af700e67b6fd740d52b6bec53ad840a94f31169d6dadbe51fc56a45b3fb95b7363e06027ec79d84a0751501e4ce5afbfd2be1117af68a171593a2ae7f2517eeaf3d0a26ef542845f9179278f756a8155454f79686846fc0527bbd8cd9f42ef1287e06eb851df7c68cb72bc28b11fb26f5a0302f4d1431d9bfde843ef7c022000d9322e65464fc3fd94083d6b1d693fc9bcd311e078cc6fb4bf837c86b1336509b0c03785ca3629140210823f3a34e477e6b53113a069203acc49a6df56466be294f", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000204570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a701de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } From 61899b808fe61e15827d8045211f25239a05f41d Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 19:06:08 +0200 Subject: [PATCH 33/54] add benches --- .../benchmark_run_meta.json | 11 + .../results_secure_agg/crisp_verify_gas.json | 107 ++++++++ .../integration_summary.json | 244 ++++++++++++++++++ .../benchmarks/results_secure_agg/report.md | 214 +++++++++++++++ .../benchmark_run_meta.json | 11 + .../crisp_verify_gas.json | 88 +++++++ .../integration_summary.json | 180 +++++++++++++ .../results_secure_no_agg/report.md | 188 ++++++++++++++ .../bfv/honk/DkgAggregatorVerifier.sol | 90 +++---- 9 files changed, 1088 insertions(+), 45 deletions(-) create mode 100644 circuits/benchmarks/results_secure_agg/benchmark_run_meta.json create mode 100644 circuits/benchmarks/results_secure_agg/crisp_verify_gas.json create mode 100644 circuits/benchmarks/results_secure_agg/integration_summary.json create mode 100644 circuits/benchmarks/results_secure_agg/report.md create mode 100644 circuits/benchmarks/results_secure_no_agg/benchmark_run_meta.json create mode 100644 circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json create mode 100644 circuits/benchmarks/results_secure_no_agg/integration_summary.json create mode 100644 circuits/benchmarks/results_secure_no_agg/report.md diff --git a/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json b/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json new file mode 100644 index 000000000..d847b317d --- /dev/null +++ b/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json @@ -0,0 +1,11 @@ +{ + "benchmark_mode": "secure", + "bfv_preset_subdir": "secure-8192", + "proof_aggregation": true, + "multithread_jobs": 13, + "verbose": false, + "nodes_spawned": 20, + "committee_size_n": 3, + "network_model": "in_process_bus", + "testmode_harness": true +} diff --git a/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json b/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json new file mode 100644 index 000000000..945d39c28 --- /dev/null +++ b/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json @@ -0,0 +1,107 @@ +{ + "verify_gas": { + "dkg": 3042585, + "user": 2972941, + "dec": 3553811 + }, + "source": "folded_proof_export_plus_crisp_verify_test", + "artifact_sizes_bytes": { + "dkg": { + "proof": 10944, + "public_inputs": 480 + }, + "dec": { + "proof": 10944, + "public_inputs": 3552 + } + }, + "calldata_gas": { + "dkg": { + "proof": 170076, + "public_inputs": 6156, + "total": 176232 + }, + "dec": { + "proof": 170076, + "public_inputs": 17316, + "total": 187392 + } + }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "secure", + "bfv_preset_subdir": "secure-8192", + "bfv_preset": "SecureThreshold8192", + "lambda": 60, + "proof_aggregation_enabled": true, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": true, + "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.612713375, "runs": 3, "total_seconds": 1.838140126 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 2.183483124, "runs": 3, "total_seconds": 6.550449374 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 1.943779167, "runs": 1, "total_seconds": 1.943779167 }, + { "name": "GenEsiSss", "avg_seconds": 0.804676139, "runs": 3, "total_seconds": 2.414028417 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 1.468442666, "runs": 3, "total_seconds": 4.405328 }, + { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 7.19246175, "runs": 3, "total_seconds": 21.577385251 }, + { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 51.025217958, "runs": 3, "total_seconds": 153.075653875 }, + { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 6.830379416, "runs": 3, "total_seconds": 20.49113825 }, + { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 55.386840874, "runs": 3, "total_seconds": 166.160522624 }, + { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 8.455168041, "runs": 3, "total_seconds": 25.365504124 }, + { "name": "NodeDkgFold/node_fold", "avg_seconds": 16.317384, "runs": 3, "total_seconds": 48.952152 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 19.080945417, "runs": 1, "total_seconds": 19.080945417 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 50.556414917, "runs": 1, "total_seconds": 50.556414917 }, + { "name": "ZkDkgAggregation", "avg_seconds": 21.026504167, "runs": 1, "total_seconds": 21.026504167 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 52.175752756, "runs": 6, "total_seconds": 313.054516541 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 145.216893416, "runs": 3, "total_seconds": 435.65068025 }, + { "name": "ZkPkAggregation", "avg_seconds": 105.563541209, "runs": 1, "total_seconds": 105.563541209 }, + { "name": "ZkPkBfv", "avg_seconds": 5.966218292, "runs": 3, "total_seconds": 17.898654876 }, + { "name": "ZkPkGeneration", "avg_seconds": 379.488959389, "runs": 3, "total_seconds": 1138.466878167 }, + { "name": "ZkShareComputation", "avg_seconds": 101.477507611, "runs": 6, "total_seconds": 608.865045668 }, + { "name": "ZkShareEncryption", "avg_seconds": 288.672301658, "runs": 36, "total_seconds": 10392.202859709 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 305.775330152, "runs": 3, "total_seconds": 917.325990458 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.114692638, "runs": 3, "total_seconds": 0.344077916 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.34096735, "runs": 5, "total_seconds": 1.704836751 } + ], + "operation_timings_total_seconds": 14474.515027254, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, + { "label": "Setup completed", "seconds": 3.246619875, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.239166083, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.004966542, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 157.906869, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 1350.540072166, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 1357.853179958, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 7.889468042, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.0710175, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 70.07983, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 389.344160916, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 1778.650297542, "metric": "wall_clock" } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000be4031062af5469ba000000000000000000000000000000000000000000000000c2b34be503df778500000000000000000000000000000000000000000000000d7e8ac169d44cd819000000000000000000000000000000000000000000000000000226e9f937dc8400000000000000000000000000000000000000000000000eb2c7b5a6fbbfe202000000000000000000000000000000000000000000000007e63ce51619b2b03a00000000000000000000000000000000000000000000000cd2bc066f5bfb7eb0000000000000000000000000000000000000000000000000000180a275d95616000000000000000000000000000000000000000000000001588f71ba712f91cf00000000000000000000000000000000000000000000000f5e274bc9e3112d9700000000000000000000000000000000000000000000000ce2900e6f8a2b3ea70000000000000000000000000000000000000000000000000001d450a0feb34a00000000000000000000000000000000000000000000000893eef95a46af9b6700000000000000000000000000000000000000000000000610ea1690987e94780000000000000000000000000000000000000000000000026773ee1ccae29e350000000000000000000000000000000000000000000000000000f7de5dd648ef04d5fc40bfcb431dfd8b2fe77b6946e66eb7a8691ab31b9ad300d29366030dc721ad9f08fd9c1205bb5a208829de6f760b8cfa7311a3d4e9012cc8357b8d6cb9099a4a7906202912663409413b8f95db686885add4bec427fda8a078948d4d5628aa9359e286cfd9e938616af59b08fe7e0ddb1670ede6046a1c0c58e62575dd064b6290f765e9ffe9947c9677e07f369d130a04e51b428ebe31ee0728043fbd0202992904bb4d2cb55f641534bdab7fbf513e59fc0fd094a50116faf966cd71266144d996fb80a1d67ebfc31c554736779b58eabe68a9b77e288d8de098bb6825d67e8f8af229c06cda9a9e53c6dbdb50d895d4a469a46daa56f6b37a7895131925d6f35fcc62ab791e2d95d20ec944f9f2a744da733c1d2ff4ec7742d7ccb809c38965d7317c3eae99030b673c6b7311bd610393138b0b54fdacef3f7dcbf41d5858342b0f9a0496cf06e00856a1c613c46164b150eac569b07e31bdb1f5c50292ba9b7110cc200cba66dacb3700ca2b93cdfbcd99547807cad9a6778416780c0b613383c0e7b7687df425fbeee700961f6e6f8f1cd8edbe2ded521cadd5972e2b63224fd73010db4b74ed0f3c7fbec0e1c78c7c30ae38e3fc7d58c8096e64048dfb1400bb0cb58ea4adae69d667fe92b899b921a5dd31783f26145dee63242ad083e6fdcc90989d2fdf57b4c9ec187315d9e8cb8ed50584fca7fdae08fbfd2d34411db2b5c57b6991d03f887123ab0fd0a5c450c48e39357a053112c62f992f8176cf1c648f258750d0a47d97212b450cbab15c3e0060d71fec9b39a7bf96280ba3ebb516bbb4f3c8f180026ba77e7a1fa6a64481c98c5b6f93781c69be3a23ad733505ea5bb254d533dd64fa7a8634a18f0b51523739e32a37ca9e9919a106dde1b8992f2fabfc973b5f38d7d3b1813220d03c4f3dc1a094dd72bc5b2b5b06cabe3e4f4af547834694b5aee1c436ac8bdbb8a84625ad4a9cff3ce92b824416d14176d0fca6bc452e100b2415aeceec371d150c69c78d1af5bf854d28fe8d0e10789e44f8e549421d9e46bc48990aa34a13b16c579e6c8953262c8ec9848006371b9b26485ca867806e2305043527f33c33b2e96c25acdcc69fcb04005ff52d17668a477bcdfb06db62e47445cda78b5cb6094f99cc644ae2f97cb48587af2403fd78d171398769e6386a60ec0c1aeb0a80bcb74f33f334387a65d27e233e0a989c1bf2cb6b7f44ac3665aa5c0b0f13e6fc6653d22c64e2493b2a2c74bbb704e181e8b3a2053a43b7556861d49fc25383c3c7cca711c753082fe51d939e6d1fb4f93e8b2e2d170363468586d5c3077658532578c9a139e0c0212ed1828504181b7790dc3cf4b61e7f986479673f74fc9f5881065cbf09321ad9d4851c9f070b98da60896bd3e085d80a970ec25538045f990b396ae1344ecea124ceb934a110b05412c6cafe9c6c7aba2cf8bb0a5b6220ac3ea6c2fcb41396ce23ae5d12ec15fadb44fd24e645726cb35b80b4f814500fe91de9f610fa9c2fa48a75737ac721329c7803475ba0ac9b7a68162ba81c2100c1b1ddbcb8da202325304f861d7a0ed2dd1b91db742084da868504df566366ce735028525a0ee60036b35217dc2f08e23dccc9ab6f4868ff67f443a726b1f587f3ffcdad2956c5deec225b1d3dd3305a4677be71c9cf4a5edf2550397caeeefb6a7ac93e5b337df2ee7675bfcfc917c1c596a49a1ab8405afff2f51b986aa5a28b989dda03fe2a0140506c8a2e351951c408a0e5dbe19d8a0ce4722ec040547dc71cf953586c0f10fcc03b4f798c27184bd60837efef5fea31825f46e8cd3e5c885724ef62cb95aff70db60b7fe40f7ee3db53fe24b1c8e0758b724eef95d0a9cb915d6dc2bff7c8288b372fa8e516442a51a759f689d8ee3b50872c5b37129226d01d779f3dd5d915ab340d104a26f02a95935c07ab2c35fcb8660e460e42dea6867fcd34b8ebdc03d63d5750a21f121eb8398a7144cdab0e4b91dc7c98daf03f94227dc1b377932541c18a87c8152b0de3cc7d40cf251e332a5f3e0f4466bc5c556e8dbc645d0510566fbeaa3327a3da254b540f97f63be9bcbffbc55f3c2a9afc8800a89ec392697488e58d7c0a7d29df822188a9beb8800d77652fe24220c533e36422904896121cf80ac4f91947f3288b60df0b73270437627f79edc6c4a50a290ab04ec8a1cd4bb653c08507dc3ab933d108a7d35073b6e1fe66bbfbd2da7617e217fcbfd7756bebcb3c931a94932570b9f249d5cbb1d156eaef1dc3d8d406a36389fa543559c76601b4f31bfa3d064c38230b516295b9b132223a8878b42d7bae00e11b6c0d0de84762150ef357d10f3456b0624ead2f3d5466a33e94a19ce0f404ceede01daae1f9cd4704359a359819d3fd9f33fa1a27725840d32e212f8772e10db8e7fb867a0e73dd2087bf3e7673fc1438352bf3800d80741a3442c52246c7e383e3d1e4b475493c1a54caaf8109e954d3d68caebb90ebe3be233e7bd0ac83ab9dfd98a910b8995b2747c909a5152de94ba4499730f822670d8d8460b5b2bea686e152345934d630024a6b6a55a8c92fa5ba32b22ecccd61111fd1328df401dfb9b1bb40ebf7211a28dfcc09d7227a314333fe16e87c0d145c6b86e1131d1bcc2c9c282a10c97e69053812f28048a2e8592312619cad05387d764af40c7f606f9932d21cc5c8a4512986aa893611720a415a1089ec6c94ee994b5994f4333e64acdc6b726afa58d229641c15cfe2ad9aa6b07b0c3d714492f0614a36691c27dbed38c4e4786c975b10d51a3badaea2f5953f9d8fa4b89791c4810256fdcf8c0a5b0b1d04f5e1765b08684e7e1e5be7844742c279a3a6b721ec4abb18e503c13c002f26bbc4ed591c2a48fe38eb62f4ebe639655798ff0cd6b3593c45c254cbfb70fe3b4c57ec41322ccebbb8fe78c3cd6e3afcb83caafad2dcbccf1d479f8487d34498f0d7a29c9d2f4faa41939be6979721858c284db8748291f1f59e79cd2076fc2028a838fd4d136c2c53a95dba376d15bb809228ede99b62ff234846e8d79b1630525424939d0eb735ade9063bb789a10213b88a646377cc172d2362c124bc16316a9da602f112052bae671daa4814c3c557e4b30e85d6631ddd7899949657411440439eab17241f28238e18a8423428268bb2d8ac2f1bba7c7501504f7d74c838fbfbf6c4c32f7a77a45907a63f88f909a829e1af089a0a9861f364977984ff8648c17d280e0c5c529d0498a7d069bbec58923c0a1491306b5e2c4a2e89f27daa4649093dd41d7d037bb2ab62fdfbf67e86982631467ac4d0c0d803b0b5b4528947497915f82b11290cf8c218c0d82a12ed344569aa01de3860fb9ecbdd2f17f5c82a21a3bf021257ff2dd7acfb3f390b41708a64740348fc4ec134cfccddeac260e3057a430f1db0beb66364702924b80fea98e44501d8eb7e4403d76a08d1c572741bf5060794415f28e36586ebeef7f511eda761acd12ff8aeb5ca3b58b4c03b44e9ce372309b2b9f3b8663df86089972bb354c48d7d38a2273c466a91cd8dd2d71fccbf1ac7456664cd6279daa94c646cc9ba17dea5c65590eb5531e1fdf991f2e2f2f415469909610a37f62d9ee59a674f81d8ebe78336daa033101f936dd7a6d954aa0fc521283d08ab601ac6cbefe31b1dacb8bc79967f81103f843f889cc0fa0a3d2051398cb52abdf4bcddd3043e6eaea0f939cc345087bcf77b49a2c19f4ab6db2205c0f3465bc498d1b1f9f2ee07875504fdb9f7c1bf1231dcd49ec059eb71d22f5b76bb0f701a665afb6b703fa79852e3c6d27371d14dabc4eefd3072f113fb0390668382343279faba659117b644b34a51c88a73067a046ab7701648068e810bcdcc91081e9e8680a197c17e065dfb2e5d4e5d616fbebb6b1df02ae4ea251425c5822c46f8036681a227abdf7fa7ed84e6e15257fdcd0830dc5648fe30e2f417aa682b0e494c543afe329b4adae6a53b3fe04ed76f4de0d03c8a4d63cfcc94237749ff882b542700127b33317658b312073746c450d3ea58b5ed6c1d7a767704dd753702f48e19b72915d755ebe09547183476093c2529d5e2bef589c0d0b32dd044650720e56a65795f0a7403068b34ceecf040b774135bedd43b7b2ad11b0c434c471ca600adfda1e2180858f5729e7d44556a6113aa9d208dd212f908862a637ed4aa3fcb43b38dbf481191fe72d8784ad72903fa2157577bf6dfb5271b2471f38190595c7eb3b56b8e023ba32ff699e75896bcf3bd192d1716710396ed2112caa373718b77c3f77f34ea151597b053d467b35db83defd031475220c9ec2a28b6a362886f375eadcdef88b87b3c57b84aecb429d0573f5ffb0f3624bf661704b94ccddf42d78c48a29e69d2d13d2b4e06a67ac2f2bb208636de76a1afcf08939ece469839835025f0e579b3b394a56cd4cdb125f148764a702aac39e4a01a361644723ade9134108d783d0c26031867be39da49fc71d03fbeeb48941e6416fa54ede4a2d334c937ba433ff61ca4cdd3ad87f4414eb8bbd453e3e11afc5f0a35a4b11749feb9912453aeb8c75c73e1972f9073b26cd1486d39e26d76fccd1524a7d0b0563592edfb89653413932cf5721d415a98b4e08b79ea6eaf02af5a01d2a8c3ff6244baff5d2d02ce87f6ffb18a6580caf33eee5da6c901f2ac5e8d0ca29bd454a302707be876a378d621013ead7db4e43a9d667a313a8af2fc289703c66d20b6849b4fe6e2f5fdb75b5d8fd69b8cc883ea68f7b54f0790ea3e947d19fc5927c8cbbcbd634ca05bed4049ef4eb11bf42eecd3b516212e096bc5c3d904db39dd8f4b5a4eb81a44e53bab45976e769a43c7a20b462f2aebcf97532a461e49bf694ada5220e401aa192bfc0fc9bb882e44421d3908b9edb7a4a711728922d6f34ba9c86300fbef97a31b5086731356dd51608f8ac4ad2903d09b7f68b82cdd60efca7e5f46d48d09109faff7d37f192869e76725731e44380e659aa7660f3350569c9db441fe0e242f2cbd3c5676f76944ea842c2999d68492c322f8de03b6be2eaab796d530a10a4b25bc4219545214c632cb3c90dd7e36c02a47a8b418eedda0eb2f69d1baa29b3419bb616b29e8cd39ddca7c7f4d35bb9560f7296105f32a0e70c90a955198bd14543c67fcf7fca5115b01aae2551ac66fc040315e2a5fd13d413134f0f7d3c0bd18439a56712164cca1e6fee01fc8c2cdc86d13502b3fd3737d7297939ba754e877016d2dc5939058fc184b7f58b26af308aed8a925b8a94bb711a388ee4fca28348b00e0557c50cecb8fd4544e6d6b5af6677968170521b98bffe10c997a90dd6fd119e62434550dfce1505e3ff32addeae9562305345045e29180416fd0001463ac12a9dbcad68e044954fe604f80c2718010b6286b57d3dd3799af96ce143c0d2a04b02439b7290852f4f4e7073cc8571fece212695728e28841584a2f60f315dff91376d737fe8a65a854e9cfadb3b1a63aa4025c333b7cb9e24d01f8177fa69403b0aad581496c71f25db147b1787ebdb45a25c0fe2130eef2f3c55fd9eab3a89be571e3d63ff6f03a41dc4a47cb7486ba1224fbeecf0f855f6c2e954b536e40228543831ced74904581f9356e75ded5f90d02d50debd5218243359b6f38f9aa0c38a2152cec4839ea5ba7cc647e3f17ad4e114ae3ec7066e1c751636b31a4eec65f271286fe2f35ed724fde80b37d2181f42c7492240793e97702e934b62faeb1de66370e6110a989b8cffd44ad68cc6653263f9b9c1ed40bd3deaa1d80e8cb20512d3591e1611bf64a4c06abd7defaa4b52c1c810c85664e8ed7fb89feb7343c01cd638c0a5f63d119bba094ec411c00510aafd0e3a82da29ffbef328a18990ff941a97a9177a8bccfe5e60f17690a774f1a152d6d2524bd5edb7ed8e30d6d410ef11c62113d3bd1cd861f101b1d0026ab140202b13c0c21d5cae1537aa9ce88b574fb16e00715ff82ddeaded5afe48b6c285674603bf9ceb6f57ba50c181cd716244605828d74c7a949d227a698aa05fe15af20dacf7ca7811b7df2670cb8b7f91c32340afb13c6d1d6531e0475c2bbf81402cc09548d27596066c98d5cd270f87585e0a534947697ccf2350d5e773a3014159212f641b1722657797ca6189aa32918fe5ef8835f1d3dbd85f4e6bd673f1aa0132c34791f2d2cfb24cc7bc3f7f43a9b8211d66decf494ff79ae8e91c72d10a3ffb79b85a84e90e572ba5acbedb8754246c0531e5b12d492043779cc8d0e0713f4a81c960a8e60354a797401b9ad06d37294c43a5ab62a566d8dc6e859fe0b3e53294dfa5d2645c1db407f9996a33339bee0df57cf4ed5f822e2b3a63ae41fcd7e4e0e2a10ef9a1e4786a6aa63c6bfcf6872b83592105adb71786c056c991c79fe5189b53521f1b3d2850baceabb6cd9f12adfbfaa96183fa43e12ca72b32e943df706dbfdebd57135e75056f0be276d51e190da31143d741836e827aa22097ebb616cb6cf4adf5e035ab8d35e5442aab3330717c0ab5f77902d03cc98d42d2b49e29ec28b39ecb70f9ad99edd8f1f1787df8dcb66fde0357c2af545c56815081b036b8ddf90a266d46528db02e603ddb68c3f76fd034e64524c9bc672390b451d3b1dcc9c967f291f5ad242381aa90ecf15851606412829f6e22d6bfed703fd8cae5a40530831a19cc3991b20b6cdc4cb716244af876033e2741012a3d30e5acb082fde25051476252d6b4bbfeb1c6d9e9a3c8f3a1d277eae36e458c20409e74c7314b02800b3e5df775133e0ab515bffcd96353710ff873586a68f22ef1c84883a0bcfa37da9f3b60925016b56df268067ceb4b1ee2880ccccbe7f67ba017f79b69518a3d6938e48f4f1b91096f65cdc0f74765fd6ae811b2422e976c0039fbe249148f354b79079ef932de9f67d65334a3bc64fbc56f1d615a0e20d7b182f773b8bfa7b3769c2f32474b0560cc803d45ea7189cb72436048e96e691631f70eeb01e87c7d084152f7ec29cc7353ee482a17b1328d90d77688c08ca597a2eb6ad810846cae0b76021d37b510857a562d73698cdbdc4f54fec9050e230da2af550f98e0e00818256856d0983d75da2c79f0bcbeaeadca8483d0b0ad2315f0f3742e9e70f3abbdefed8d6cdd306a678952bc1bbb0dec66f73ceae932922ca0af9187ce31f3a8dba71ba83dd28e38263f4cf8203e52c0277049672b615ec5703852197e8f0f8fa6230f2b734edf0d6e6e72ddc23b9b59da7f7f62695408329279532a9f9aa192356967ef60667020bab4073998a31581b5aa2769e5dc4708329da847757780bde50232030fdf6b1807fb98193b7e3cacaf25a0d7c4799bf9b1d48e98daf16badf314d580c73d32693de4d749a2c748dcd90d7c5c43b854e7b1fe6fd7209d4b47d16eb531f7cbde3a17ebb240b12fb467d0c2d1e873f4481a40c930a7d752cb170079ece62a0859060f81062f0fe6be060cb922e3011addc060a5d3928b2fe65fde80aece1064a6a3cc5da0c9f3a7e98e2d77640894b2a18c2234fe7beeb55b241a9cea1abd795d4d3b1173d4789db2a0eba90a09078b7b29a2327ca87ba607e5e2ce9823319fa10845bbea94dd7c8601d050a2d9c230342c007091c45a7604d4126bd5ba268bc6b96bacf6eebca8e357f96dc9196cfbf1262208bba9cc8c4468f9e020ab07a534bbfb4e19e9bf2d1dd2fa7fa46a807c5d2191998b29eb8c1a2cc49c5d89321fcb84a58d017fbe187f8258450d6743390da2f2e330329d0cd738eb9accb2757aa2ced21ae3be0641306c8d05e7b945717f3a22467da32307db061fdb0f1b7ddf119f9d4c70dee698b9851a5f7afe39ad0517b0f30fb81e286136bd256626fefe97ef6865c712bc0ea64b3c05d5c2671bc09c22329005bbe9b8cdbc6ebab0727d1b5f2be95500317aab579466744c67137c18107dcb8773498048d26c2ad069820b1be6997bd7d0fd2a9efc58321278a3815ee1290bc714c460163c534f2a67a207b72771b9f75b5e09772cedcc841485c3f761c34f787a0fd0cfe3a349d19616f1c8f2f3408b79eab7402edb8642e4fab97e81b84f46a8a7afa6dda8309ff50c780133c4bfe41391118ac47da47f6c64c65310f466d93d50624cc5ae498e137766c29992e4f540831934b104f34317917ec361d725b9c2122fd72318be3b95e1c228bbc5ced08aa9178d4c09f1c3fdce06b3d0ad6aa3ed19456e163d727406b9e1e12359a8e1ca458affba8795cb626ee243e056c563d07018c4d5576310ab32e0ad890618d5bd0b4c340df49a3324601e0c61669fa62830383f3ead58b2360986baa7084f38bf54e4ec380d9ca67fd9563b61c42ef088fad28f92bcbfae2603a3bbe0e3b3ebea40bdf7f0d1b00a997ff5e1b15e487a2745b2ad061b89772c2caca6942f8ea111d61349cf64adabd842845c7037ac83db56f677018835f0adec97a2e23d615a9affb8c114ae30474af028d862d169ab4add11ac133a9f0f86ed72ebe6784249b62cec4dd70d66d842a7004271fc4a532858f1392bc3f447beea906c3ad8a2600c296e80c72501f1ce9d0a3652f1b51b6707c40d8e4b03a907d3b61b8d77a6b28b0361d25f5fa852bedd4308f0d1bd4617a908f95dd7d4d61f02b6299c2efa5daedf8b276ffc60fe6ed7c74f92aa265ddbc2997e483ab7d72020b7bd63088c46ca4387deacc6af4becd1d514928a4e52bbd565a9ff3ea24c8cc32653327868e836d1c6d70d9f2dfb85f8ad71b2937c47ef2e32b728d43d5fde76a027353ff5715db2825010ee75c02f650b0d31dd46ebca018f446acbdf62a40fd7f462ce908ea79b56992be5ee319cfe0c5120dceebbf0577e73fb6f9522ac9291b1aa478ace1f77bd3c27ba8dbae11ac01ef1e311c049104ff89d1d03a814aea07d10571bb6c3a1a03d199c166c9d89c8a6f0d8817b59b5f96711d9a76253cdcd4780752bdfad35ab53f139213516a0f590206ab60816a58c3d37c7289c38e5451785161a99fdf0a8ed655253225d2a472a4194c4efb32d4241593cd470a69d14bce7e77d62b3516377b7714d7cc72c528dc2147d770e033a46e87c8dcc59d1f80ac013a907d88abcb8ead6dcb38b0a316d11b55f328f6419cdbce83f354d7c6c21ced11005f2e797af3ab2b493623098b0408c68bd7ee4105b99ac2582ac6934481c35276e0e97f4282d52de0a9c551d6a11cbd2c6ac7b94d2703b8970beb49631fdf0b185ac097894c055a4f1e25b6bd0c09d60a6817d44e48213af231b0adc86ac26551a5ee1cb95a63ff0f6266d4ac6d08247cafa25b0bf66ac26718882c81826140e45d0ed00a923d0392d7b94d5bfc1d3363dc3370c556c4deb3aaf1f6062dfdce1a64ffea0c70a7f3c4b596fa5d8c0d199eadb28c99f5d52c29d62d8da00274a0a7f995c4986424d1ca434c105a881c53e3433fd5850470241659b7cc079cc4c24ad05a0ca557bee7144948bdb43b178afe29a44f2990bfa8c1bc29781d110d55e6819c13cf141a870b45875abb402dff7d7b76a82e59083e99f95151f279e23ebb951eed4566d885e7266529eefd295b7597c2687529f861e6ccf0cd557dfb67ead6417b7932e5986058bffcc30d082b82b37a193b1a4707029f3537de2804a812d6163e56c3aaf22ea3a785c86b1dab7793e05c29ea6b322854a1134295ac17658626b964365cd15b4fd9ff16c027a54afc15889e3007a0348f9e7bbfe505d79a9216d794a873b4c83d5a830e5414860baa803026759fa3bcc5e37172863e6b7e4d70e344871443d0c5cab8d4b601a98111433657f2770c31c112fd57cc42c9245cb151cb1aeadd23a026e436d723a267d29760a4b339780a32c41c7f5a6510650cf324e6d688b758f2f9cebf7c19347c3881ed00a950e0a3cea706b404f45e9b1d8f608229d18ec6f55b4d2821179d341f4e781ac9094a28b5cf4c1e0c6222f49a788db9c774621ff49daa3c4200b0260ec89dc1889f9fec750ed80d6c3c2be8130ebae08ea208cb1dbcdaf0760b4412c2b2183a26cbdc043c9ac34f6b8d15c9cecb94490b951a8559b914da3e0f5a32d3350d6e7b81e3f45c78f1837ea1fcfe64e14768706b01fa1b6c4f61bb10695d7a599f27c0989a820218fe22c5676a8bd30db5390abcab432ef521b6341e7d89351ec3c66305f2c0ac6b1cf4b0ce11dc6a847e6583a96a934d20e464a6290be30d8456c1aebac52cc6a5d0d7d260f9df4bb30bd088ae6362b3eb6cd9b6049e506f33dd3ce4078afa704c361953bdf6c573203529f2a6a0070f560809722476d300779e00e14bbac5a882768ec7c26acc505ce1daa807301b883b04dba7283c5c9064142e834e6713920dd9d2d161d0b363068a731e11f14c5f792b597a0fe87b75e67c58d24d76cf08dd939e1272b756e5eae7b3c620e663f10f2935fc1ce194e5b7ef43a203a8bdaab9ca888ce83ca654d5e59270b9d210d7f64a198b0e12c66d667f7f71c4cb1314ec50ecc7d129debaa2327b182257e5648a7a27f8205433d8457aa829ff0eced68d08accbadf6f975fd31159fc4c097ae81ecb1f60f7fe9c234d7074e92b6333b0b819cecfaffd18dd2977434319c373a0e4d34ff1115c1b80a81e1a261e9369ccec0c082defa52fe8174a668d8aef64c2740e9b107f6a805616f79f4fec5900fc61792c4a71f2212f6d59f7475329eff567da947133d4f8c0c07b34ebd0db3f01aff1702e6b2c1eb8354f6c9c6dff3a4f75259da0b81d5a2a0b1047d2365ccfeee0ed9c96cf199cf703ff80c9afa6d6224d30227199193c646df631334511cb4003531d4f0eeaf19cfaa2adb9915db7011deabde265b6713610f82d9ced0fa5684bed0807b31c1b27707bb1c3c6acf4a5407ffac064015253debdefd6e0054d89a405308fbec4f9173807df2659340190744f4b5232950c2cd17188e521b81db6df3b6b1e39caf938913c13f93df8ade49a5ff4a07286c295dc83dd453eada3bfa1750fa12bef03e44f24e171a622b7212107ef4091ad7b9e66eec303955e11c51085ee170186876d7c2acbbec43835c6c97b16505f41043d8415ce73b3bf2de37811737de54a20eb9f050bdfcaf0d624f5c3dba2aae5d982d5e01e2699dbfab33228921a2876caa668275693665268fe14f1d9f127503ad77cca35b54cc3f1475ffbfb29900c23cc3b2c1512258f9bcc823263e1bd27cdc52dfd62dad4925033970d39f0d65114b0118963cf015a8d58973e76d0024ba4de3892b22978e5e439cd528bbac8ba666034052af00f9402844683839100b8c0783efaad35198945c3d98726886c596d91918427e81a222fe4426184a2070889b115fb5bf0c4a757cf56f755db917ae10525f6604cd85613dfe647dcb0507c888282ceac7b445f109811bf5f3d509d5d5455c9a9e9df1693a87ca3976074bfc8a21a1ee53baa63cca744869b825c4f57656d8a4102448ffa04b994faf0d8340fb40a86f67d1ae0898ab2ab863a763cc8ab45a58c96e02f5d2013107632608637dc8dc6a019d6af75a1ccd7b0e350d0c8e5a161ddc1c08e7c9363c41b7126a7ca47fb8a4bd3e38f0b529785cdd02109450bdd55bbcb3ed03c755337b0e1f0bf6717ca7fce898a35280edf6f2c6b47bde31b0a86ce835a0dc9c10f891ba0c7fa8d1b2c54e27d37c5adfb06ffd4c78ff6373fd724c7ce69119365cbfbb0a241de27fd2ee94b23135b9df33621272e75d9cf9560635bb1eab4c6f60f75a84009783951dca4cc3f935b6827c4ecff13f473b7c11b0f96a5be7718b24b2f0f62a5f7ed6c5161ba762869f0c8b6cc009b07cb8452ae6104fdb799767434f9ec52bf3b87f7aa35a13cfe46e2318be378b3f9bddf4ea9223442cfe05c4b18bac0e271f21fc429b31ebce90f705c31f6c19d9781feff3ecffdfc261518d454f12691da279357dfab133eb08889699f75174df8dfbbc381b48de70c80a440cb666dc0a7030f8c8ecab1c1c97d5c7f2645d60891a391208593f8d7e5aa04bdcc9d50209356eaa0695cb0e5a49b0dfd5440d958309583540c02d13b3fcb4597176c3d50c9c16dece31449cd2574a45d99fc4687374c94602102f5b46246ff60e6b37372a6809108be44bf7b89c493bf154df1373f8d9086392977ca13e2cb89de5adc219f76c71cf919bc6bda4d0fd6d0d07d4ec925b2ce2ec19e9c3c0241608419d861c096b79285ba465534064948e058a3bee97438896dbb2f4cb40c7cb0ccf41af0996f8809b176892e0c5da063c36a6930a3b17308a2c4e8dd0aaa51e72b29bf0215ca78f71143567fbf9374d7e9793e93d6a3458998b8bff08d5709f1e58f63b0736690dbb85283c9d46ed82c2d90a3bb69c84617c7c88e4c5cc9d79d66dc710262151641d45ba24f9a918510327fc2d31b426b92795ed9f090ef559cf30f1201f8d0d0f78d7898a04afc5fe83eeccc1de82945db199064057cc868e6900ce7c05617799dbef66552ad082b83f9eba55a7ae4fda9acc94e5c8ac7cf11ae65053062921b2287074e630a6f20c1fb6acb692eff24c1c9fe9a1f842b571fff806e625f4a286a5725188640abced4f491d781c8597161fd5a4bb98d90ef9826f599325be04d44fc983ffe469a70f28b4bdcd7a044abd612a4bb4ce22cd3590767d2d0f5a272080032fc298aad59b56a8c04b935c4f5f61f2c882ff010843359cc5722702cf8db27c7ebe19821f6a1fa2b45378ccd62678f2bbdba7fca7e666e3994a17792e281bab19d98bb9e4ff096b79c3af935b151cf0a796a74874ce1dac0bc11dcffd953af6e5d33e9a44f4b6b5cf11c1adb4658ce8cbf61a3469ab8a69de5e08c0469b21420d91cff9bbf8d4b0d3819332ef0e3271d5d58b93a79598e119ba1f61b8b893ef57a83c6bbd4bbf2dd11dd7688b7eaefac01707fa0fb1c14976711a468f53c7b7599ac1b6c1b36e96d2f32680e250b2f9d1f7bf7193d4442423be150dbb9b7e88f9773311590cc888b7c93146e9c869aff64029aa405e9a02de9416b1de1fd80ac6fdac97671b88ed413842bdbbe4df0a6d58597d9f1bb0372bfe1ead477ea0920fd1ea8163d17bf85a16a33328d534c93da80b340b1434000e58031dc61005d0fd180e6a14487f716cb16fe16b88a5a18fe297be374f46b99f2a08f971573d23bd50fd55207f764d082c45977064c78ca0c98f5b03aa716306bc0c1b172fcb4d88b6c2f082316863d54798c916edf1e8f676a2d3d13d2400989f093989b19720188136a6402888ca5b6aae2ebe548f2bd13b433efeccc791810119897052bbc6759c9b9c133bafda465b75a2007d875aa59572c04dfad21609c5131bd9f0ed42884c123fac7a35e477de7fcdaf2c6c15e80c7f2de1b7ad5b06fb117a1a56edab8637fd959382fe27bd2cb7b7e0ba2bf58183aac386238fd609472642e995b1dedc27f03ddd11ed8513d43058ebccbad0530dc9fb591ea271bf0b1b19ca21b1722962c2ccb3f36299db873c652e6fce58e5bc7b0ad2e88f7adec42cba039aedfc64da34a85cd47c3af5d27c4edea22a973d5d6c41ee1ce66016ea215a5276ad1b3f1d6e3a016692962a9bf07b77ebfea88e47516d890c8d21d5b21ae20bf3e1422de4c93d5b061bd8478db46472da3e5e09ed7a9fb454eee320b4235392465ee8d32e4e2d719bac58417d831b9c3dce74aec833b48574770779380a3fc55e22da21db976bdf202006299dc9dfe22aa5de25723b1ea5adfb04b8fd2bd57faf1e947f33379978913e7ec27f4e025cf013a7f20a14d910b4f4030a0b0670a5e458d75f5de7b8470964d20e02a18a292d3583a6cb04ca0b88342b61d6030e48bbc07de132df81dc65ad5b3ed95c2adfab886dc91d151a9d4edd1a1d87299830d9b3137644b4cbfbdfcf79ca04845f9f205ef074d52598a0334d44a16d0596c56820ceb0c74c6ed64b4e038380ae235f3a1e03092f703b593e90a111ff06812849a90f4292587df08935efdb3f016c7eed02d8fca01111ebf8cbad23532aff6c0823528961c992e7a60be74c514cbfeb62c3dc915df54e6ea945195ecb1a38ce6a1d6d855aa8e937ad88cc93c2350903fa0ffcb7741d5d69ae61ec19df12b28a847765063a379bf3063f036b000bf4857efac2e94b7e3d4f497d2c30192f31eeef0059b5f1a741b799eb2cad48805df63a89b47c4d7bc3c197848fcf952ce299c4455782b74e8548982cdc5e5419342df6a271bf286c4040ba56b22e7b0999cfe1bc2ab84d82a5a7561de669b30e15153a7526d19fd36705776faeb40f0aabc1ca6017427fa83fc862dc0b6bdcfa4e75acad8a5bfb070e3a38893461fe0bc647b4d478307d5eb5949e57b9895ff33d7ba47210ba78711a0a77669d04cf2a8d0c0af5eb4a1462aff2fb866e200cd1d530bd23c40ca45a8833242363eac30836a34e5a7d62438533cc839950f9f57d822526fa23da72ef4784349ae6d0ca0ef6b88b75895ca69b8163e77c95afd817bfb9f8f215f2d87954f546a007decd0dc668c647572a30343d9d12fa69845d50cd2fc6234942b22b9b6d8b6bd814f72ea1dcaea80d09e992d120228e1f67a8a4b70c6674aacd146f0d881ab8130abe104df4ed61f189f4889b10dc96d83e9bc6e872b54f626379d5e14be79625913b1e579c015c34a47d00a1043436c23bf6b4cdb23582f920d2b9b5546d10cc0a8c2d1590f7e5203d7ad40ca70782c9b35aecfeed035626e5de084fb09f64b20592", + "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c911096761830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000020efbc1b4676f6018902362ead77e27d0000000000000000000000000000000060ec24b679943ff559895776f6f11c442197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e2aadc7250d71617ec5c2f852c2e49ed3180111ef6b130ece8b3504a7e2c3d0372c30f9e94afea47a8631132310e6d72615ccd3227fba92d2ae829172e7bf1c2d24fa1f864f85046e7f1b15accc6c376dfd7c6960cfa3b7fa0662cd10007c0ef7089943510caae41bc6adc5e284e6194b797027236225a98865400f364685fd6609bd127e0b1c620665f422cca7d6dbde8d84465e9268825c7e12feeaf35902160bf36fd6ac0eb33261fdbfd25f0c3cf65632e1be186bed5531a223cf9729cf8d2ea020aec653a1abf28081da5f3283ab32f9ebac85d38f7fb2c6b251fe701d34" + }, + "decryption_aggregator": { + "proof_hex": "0x0000000000000000000000000000000000000000000000043fcf4306e2131cf000000000000000000000000000000000000000000000000c5d67d4dda3528cbf000000000000000000000000000000000000000000000006060d23795c2aa33800000000000000000000000000000000000000000000000000028ba39f65b860000000000000000000000000000000000000000000000003431e382e17ac4ef70000000000000000000000000000000000000000000000021b4003913aea921900000000000000000000000000000000000000000000000c82799839439f89aa00000000000000000000000000000000000000000000000000012d6ba13cd4b700000000000000000000000000000000000000000000000080f5bc15f3c6fe10000000000000000000000000000000000000000000000004914047fa68bd1c0400000000000000000000000000000000000000000000000f6e01eb1aec7be2f20000000000000000000000000000000000000000000000000001f55f29a8b26d00000000000000000000000000000000000000000000000e1f18a47a78f5c09f000000000000000000000000000000000000000000000005849783f26c956b9c00000000000000000000000000000000000000000000000c40e8c3b7b05019240000000000000000000000000000000000000000000000000000b6043b2268b016ecbb30ebefecde0945bf712f33a5e35c605e9140717df8327bb6f5ef69445e0d2588d882879f74634b110eeeabd79da186e72e2bafbf2112924860dbcfdf3c026565a0460bc6cc51c6b1e502aa421fe4416359884492c4229e20ba422abdf41b2b5870384f803e511662e81bacbd584a2d20e25bd8fb8f0fa81166284ee76a1c0dba42cfa440d7ffb3747a6be1cdf331190bb4b1647db3ec65dd98881b1a2f2fe7de0b5b7b7e6fffc7e1dcd6d0a7f3fb92cd074ec90146d9824a3d2c3819ec2682e804a383289c2c9f1eba07cc60c1aff1cb5a6b70be9022d5a7a47e9f8e490c624154e85b5d4fd9b49acb44ca6ac73807ddab9dc20aea1648dd55a2a2cbe2112c1693d6efe05f2d2179bfe2b01d2d969c920d5f342d4a685d6be5b87b5efb1ee16d2b93917750a1dc3e00110ec67da2c9c33cc8ed7e6edef97f126e8ecff128ba7efe8208ae73c22be4d44ba04934379e8eb3fb1dc1c03bfa8b55a3486c5b131f1af9b8e575c05ed7f11c7f427c37e544ffd78db60c7f7b5bbd4da54d96fe002392dba277c15ecff2caa1cbbfa1c0972d054420c5d8655056de823a25f82d1772bcde199450e37df722a55e7dac41a8ee29f07fdf5e9977192943104a95800976f03432f34819ad3555a2bf5d72e9bc63f9d3dfaa9288bdffa426fa258f0e1e671b5758cebbb860ae076f5d73b5d244c234c00e8cb236ea18e6dfb7a6385a2ceb66fb20170a1f3e1904f20a2378844037b1dcadf863f5ef19937364dcc3540e2347c932b8fefb1ec54c8d8e9340e464e115efc55df0e7f56f24e44a8933ba080e285d212dfef11316c6a6f955b9ffca9161b565b183fe3b2819f5eaaada5c06755bf971610b6d70689ea43421cd65c25845da3eabbc145b5f325388497628019fb3cf9bea5175a846b9a4311676de776afdf3a762cc5f00fa4d0c4dfd6804110931e5e2644a05030d33c69fb9f202e3cd2df0341d476aa14e0b4a7d817d572d8b912a899d76b2fd4299063aeb8e49faad0c5f5ea9020cec394e6b0e1e3c9a0006fc8173d358061e3d7ecb78dea762d79411830ef656b635473a7cd16c21121ec372950bbc5726d72ee3f558b3757b55c96b953702a53078156f10783707ef2ac2f9753aa280f1268bcfc51d5de10121343500d12ba3c5f6230a09038cc8631212db3c1be0036f93a42f8a72b1b0f3a175fb9c09c4a31cd3a51d90a1226d6a07e82863c27680a6c050247fbced037c94c32038958b6ddd20115a7c942590c31e77dc1c3f5a6d558f0f0f3181a433a08f7641e3a95703b47e6efd448f0e74fe2d2120c5426c502b3d727e77f80277d8f26607705bd8bc705806a1269768f4c90da9c8d09b0bed9fa2ad2d1913c11030c9c9f9f60dee6cd28733e23e4cdc2717013c759281a65f9c3c13648b4b72430d9b7cb4dd11aa50988a436c113cc112f9042147a805128cf3d0524ab1eca70d990e510686f9d81900d21ea40c4eb8bb071309f1ff7af94fbb0f93e57f882e90bfd5d336d6cb45204d92fc5183aa2da5d92ffd7c5455bdb97f512e60e118641466f139a3926b97a57cec762416b5f039aa266c49d2811485d19e5b8c6d8481def5a7b11b4f6eb0d35a0a934ebdf8e6796f00e4fe8aa6c67a91450e430ec4c6ee2de98eab9780377cb932887af8f7a412aa11c46f63ca887cba374702c014f9181150ea66878ce0bc91126b160e63ad073b1d0ae14032feea6c05413985b0eb692a5b77e2cac56156ff2b5b27245c42913b138e299113f3df18768913b59f15bdefde9c8a535ccb394dc3fca88d77701e260e114f74e35904da232b3161239a94432fc6ed77075589e1d63633ee73427a601923d78b146367f15661597a3bf8bd841f53fdbadceda0f2eb3b9faa4a4351bb304131d0a0cfa4c3956c33343e3a8dec9782b0c12111165539ca255dfbf9a578194039d795df60cb57d147f56442d382c48b64b9cb458db43145a36ef77deedf05dda7c099ccfdbe61ac7687d4c38debfb0f94a862244b520fedc90482aa62521004cb00f3d7dd01f3535b2ebbb943ae0d3d88dba55e4ffcde1b929c4e7bf3f1061d9e25aa39cdfdbd3bb070a330577b342eb8115c2d615e373169af62a93283289bfdd959324811b0f3e3ddd44eb0b9c5cdd7eaae77acc35cbf66799bf6692402f5ef94af7bc25aedbc591d063a5e469a239ce962ad0f484a4e76fd2d4f6d152b35d0eb17c6421293d4c63741ab030a1001d888336e86508ab8b14883d12b771668e1bef33f067dcc1b3eff8730a7ae50bba456a0e0f88d3eb6d9cda64596a02fbc33b4961f1485700545c72d42e5d5f39ae5aa7ceaa64f38730de63b00e5491c72a5d315fa7e6b2f997fb8c32d691022ec660e40c3f79b2e4bb511be4f3e840d4a09bcdb6ef1f63ad37da6601137019a92853119af6d763fe02d9d5da4631b0b2fca10cdac6da89c06471694373ca09119b663aa8f2da003491e3592dd081f2c819ab7e65d912dafe80fbfe0b9d827b3a7d97730ca3997ac5dc303080a9f101be695a1ce3b315c5b47ffc36d829c402e443a78161c63bf19ebf7ebaa362fc71709980d1ed1134fef2ff9ce94dacb7164611513e3efeae83e976603751e74fd1cd0954090ba4339542c911dca7c1e551c1b621fa6e43c3f76af3194bde15ce001879243c4b5d2302612ea4faa1eced4812d0f8a6316825ce217dcbf7f12d932071cdb0dd595478abac873da51a48363ba1bbab6a7c9d33811c71548fd889f5d074019a5fd602e278f49d3a5d78c3eebfd9b15daf24b30eafbb9409820548b5c28d994930e37cfc0ee4f823768bc178f2c3c47c8b0c7f68e18197f4d1c0d2c14296bb2e2d8212c4e41962e429f66ae14fc0b1f5d953fb1a5f5178ad9c3457d20068064f3d23ce29df576e721b4aa8128f2a3a2e0989237b8b4dda0f89087c3252976c194886fdbff8fe106b82fe8cfe21ed4601b6e45d5e7df728f5df112599417f762f1b74d77d040a8ad0e44b935c58f2dfe77aa11f4d20ae469c70b1ea777211f3445db92471b04c84945c86a302d96cbee2ec5da3a441967d52df4ddf5d720fd042f565f49741b508380c4244af3ee17913bae8c9ab320459fb9e5ff6fdd0ac44f8ba61e9e6142cc8e5b29266617f01f913c653d122a01b954c49d601cdc2c5a06c02c720bcc7cf83013663183d11f1e87a2bae50b6521ded294e54bc8f029b1a9f11b22519e4cd52aa674780357b510cbad34face5080d1f612288c27c4291773537798a4fbd7dede6922cae8054535d163609a0d5daa8290c847b682a628798200861f4a7f155b7181b287cb8364a1c034a67e5af9031435f02ae78d0d2064184f366dfedc738ff7893eee8e5dcf0127805d813abcdc7b4a7377e53dcd006e0ac8ea735f93588ea0a71676f3c383859c9beb401c369dd53893251285121cfa07e3616b72cf7fb2edede8a60e8bae1b4d9406e467a75ed676f5c73081a4108b9f31a1dbb2d80e1b4692df14a9ce0f854190ec40a3769c1c738b3a430f5503b15ad4808444dab185ba8e4f56ded101f67a45dd451eadd2395c8c45e2726827ddef201bb70c9c3aa8a54c25d99e908c0ea19b5aa121567061312a5f07f059137b6f541daea8980948fb005bfa6d7f6e369095274292b312f91e641a8b9ea4225d759d940cb55f0a933e46a54d811891c1bf7affd9d037f0667dc58fefd5650fa96cda5692e496cce3fbfcaacf903dfdd71009e62766b6c87f3c5b7222108f0cf144bb82a02a2496ce587a7a23c89452f308e6792254e4d6d8c05b3b2ad825287a68259e53a09024f4f42b7e4c0154e6b67c580cbdb69aed8e412658585d9c16ae813774b518a16cb72e9273072f16fc1e696800108ab5ad68fefb495e92081e9d93ab4c44276fa03bd0ddc6e1cea3826569ca4be8df330d800d3159e9c7b004b02887ac694fdd3c3ca859ed6a55a0dc816f518cd898c788b530c2533c2933151b42e7b40ee1484f3d8bb497312cf4b8aa083dc199675cafde4aa229bb847506888be331d090e12335709ed8fa88d2a3abb1bcad1b9bc912b14163e117f2d6090e580d58e9762e20dc5745277328cbc4fc3e68b13cd5931d13973bcef490492da90e2511ba4f72bf9f5720e54499ce3c0a9bad2a88789682dfbd11fae3dc7f141c57158ac4354f912bd8f94543be5ed9144595eecf5d5b305d5a905aff23301585291ceaaefc89118816b3e053546b4974415cfe209a9ccbcf6b6d27f802e8193e0b015d0b91d9bbc7c16d3f673ec9ceb25f488495cbe9f65e7f64991628d81e4ab818a70c6a1f5c0344f972e3d2580118db18687ac9c61febcd613083e2fb10c96092cc963667d90fc249bb5f0246f77efaa9a624b54810f414a01f285ae91f5ad3517c22645f3a908264c6dff27a5fc3e83513e4dde1b2b4d06e7c0c21212a97c5faac94c396c3eb75609b19a9fdb04d70a404545b7688f3fd1664091e9e01d30bff59042e8eaa895df34bd4c8542de49f23b3b05247e2a60ea7c3746c5c1c82e8522d6fbac54bf45a7b88d466e117d6bbdb3bdb04404be881407c8b347128e4ac64753224c8518132ba80d90e9e36d0660a6420d8ae34883a2d3d3844f71aa13a859439f3ddf740e056cd16330f61f2f5f73491bae01256cc374555869e25aaaed0dd62f477ce2b9fa5dc14b4de837f1fc50e0012ff151c99326b80dc2f08ad72af198ecefe4a5bfc090511a7bd527573e9a71868ae2de0205e2fbec5972f2a267989ab9cefad432cd219000dc7f8ded53bb25544c5eac1c808f9ef87e91e356e43f116f8abf3db5af9c41eed862fb8bbb46ee67a9fe1d14c2b375a4d9c282a9272a09ce77f3a19320181606fa006e4809ead7028014bb9c0b6a272e0b02084794cb26bdb23c1c92e23962a8f80b9347562c76fb9782b08be8f480d4aea0d27502da998f8a4d9521fa0bd2ce332ff8d407451ee3189e4066870ee19128315f0c4621e1fbcbd85523b1b090cace6192c14beb904e071b3ee2c3a51322d9504dc793bce7dc0321b82cb0dec5c082cf850d14e22ef3af036646db4b3d03bb729ed41cb39eb0aceab2405ba0f571af49ff5b88af530fc40aa13614a82d4de4b0b1f3dda6e318d86ffc5181f1ae26c6720d6dfeecfc1d7d428bc80b38889edec04c86df292c6c2397538f796dd154e27f83c090a3918842c43fed2e008760a58004093996bfd42360e58e89a3a837a4f024d9ae4de6e61b9fde360f039ae79142310592732dc009d878c5e24931017ba6e1e127fc916cc487dca718eb0d207461accf461484e7f577dd7a7225398a1a222fbab01fa7b4cb966663decc6f86e911b213dd20e59ecbe262b4f78eed6229f8efcf1181f85bd54056cb9344cef882a0211f77acc433cbc72f660b53633076591101a5debd1c7bb75c477cf57136d912f1172c70849b5c0b2da382b36c1bdb2ec95fea0d963942e49a0bcbdc042fff82a017cc2a52d4e27bb55c769c4c8f25bf40863f646ce50b306e6fccaa3a2bef71bc6ef8cce6579234acca57828c774f693a58271dc056a44bbe3a5711292d86b1bc3f24eacac535dfa3ce20d18337450e14cae37fef5a82affb6bbc9048066902b1445ab1e06c254f4459768a63ee8f16062c8eae5d8eb828f3473f50bd1c4c716dac7dfa5ab8b4ce919e636dcf12a4014526e76d8876e9608df30f9576b5c03127e340a8eacc9bffa81a0a6ab84040c0c51cfbce8d0e898c8468a467c7482a3121bf8817f9896eeb0b803864731ec829f424f7ccec392768c521b4618a6ef3a1f864d74a4a1da3e2f0353e14d3341967d04dbd8b1cc7783f9be1937021b6d010604c7fa35db4d3f9e47cf064b7294832f18521aab04892489887ef26dfbcc3514a06665a91d11641cb70b9ebd9ab5998607a57970b30ed6e1abaa8d9a3d09fc0ba348500a8a70360967e7b2e6506caac7700bb250ae6352a52c80df145e23931ad6bba502bb84c26cc47683d959ab382234d2a6b50288c3a9f57be70087244c00cc036e2ac4050038c045f44e1174ccd51ae2831417eca2ae646ac4dc7aa0531250cc026492f0837dd16254415f01076dede1d0161d12a71091d973065b5b0927cd6e7b6887b563f8e8e38df1a2cf49ce127092ae7f14c16b82eb3bd7c287fe23c93e46dcfff700ddc4db7f0acce19981861fa8741b92a96a89f49d6a2b84641bbba385566e6db0023ca34ee25451f7835e0036587e3cbbfe5c5be17f68df7000bc03f38cbdc90bcf75475ed43684342333c98aa481bdc8933146ae091358ca2458e368f42eec2c19f1adc6c63c133152e072bea6ed1e1a254c040e1f2435ff27a5390a1f10b0afa9098d5b17e8ac84a75e0db6c0bb406d0e3e9c66caf5b32c215216ec3708f27dcb83202ae8c8b1d7832d6eb225a4ff6427d2ecd99fc1017a0e83fbe91a93c8d6ad1bcc9525d7ae40d181022456b13c271a213a7b99f0842317bc405b9565800369c7e9ffde1f1c955b9ee216700826fbee267429b2245e542e5b94f74d0e5d911fd6d861de60a9f9472adbb11091348f7ddd786bdaa3638f200fb4be2cc00aa4bb5754bc487d25643fab3e56ee92601b3d957d9969f0d8751295090f09b4524343335d83d5cb59cbe98b4b30e823fb810141a1db8b1f71032161af45ad8399c25773eef4135083c74ba34ed6e9b3f4ccd8ef4b54bc0db31c136bb4f01423231ec602e6867fb5dd7e2f864bfabd3389e116299ca683d0ba3d167f3caeb2a67f8b70fd97a454cac0decf9f04d6d0821e5304e95aafa64496b81308d84cc84554adff0428ac3e3a774a2bbf5adb3bc4bf670dd3bf87a76b1de22fbc0487db3499fdbfb393424d9c5cb42d832652a6fc484f44bf69307278f9482ab4dccf144cc70309b7d05d855221093137ccf6d29e1b92610d4100b5342a5e2400c944585d2dfff0536223c96d29a24d6b3e9c50c9110a0861c6a1ea7b03801af1f2fb4c61e033a13d3824df9251ea47f41739aa82911366142ecfb7cb757318759290a0356f8624ce873e0b73d1f241762cc941e31fb425c16da148efada514a1e93ec356d035d430e837d2bd1fed9ecfaf76e5cd05eef102799439bfd5fc20a5c7540491780488fdd25770f91535cdbe45cffd1a643d3393c2ff20ae2adb25398281111b51e2b65776c2d724c756ec35997aacdfcf38beb2283c693ab38709cbf556bad78501b91e4f1cc6091157d69450fc37a2063edf6e6a6bd6276915052ab24e1a353629dd9f290ea28da3f7c6267ea34a1e898bd279efead05314f611569998a8ffe0617162dedc7f4a8b77685228bd3665de049cd79924d0874a650138ed3fc5d669eab35ef5bdfe9bda7eaeff28c5432cc4520ff3d59c70407f6e23e3fc2315b7e8950f0d17e4845ba7a6beb01ec848c0a6fe91f99aa47653eac0088cd87378ceb2debee1216f0a3b8dcd699f2adab6bb3bdea59deb6af8b5c30c2bcb893d0ef10dab2ebc892846b6db33ddebb227231a17c6048fa74ee7a741b41b5a262d7db93dc96fe513e5dd0c99b6dd4642e88038d159980292b27b32186313dc8597e5cb810335e3af795e63d11907cab90e7d4c6ada30d42b000c6d438c296ac4d65d5e33bea8bbe7b4a5cd346524c4ba600f30a6847116b9488e4adf341a8d23847c4c67286d0c98fc0ddd2a38379a8b6876c063025972a9b15ffb456c11505c57c8339867f94cbead3d5abe1b40023239a3059b77874c2560f33907142fb27c921b1d03af6980a740462cfad06e7ffe76f8e5e12e59bb75ebeab5b420118fdd54b52424bbdcc621331f7c245bbc0d7b4eaa9d72e58fd40b21e71f558627f775a58502a5871380974b05b73b0ad148ecf99de8dda5c06ca5bd6e0604dc038ba19f12c8c3d2125db1afff20e968de85f6ab442adfc88efbfc9db2ab2c5b0f8fe1650a994f2ec40ae3bdc85a964f349c0529ad0f079f50ea0825ee21805a0c67bfbdd0693878a6f8cfce2ccd0b207f299e10855a7f85a30858d3f2b1d08f0f3084f6a16cae9dfeb648a27dadea1060b707ba1e5c391c47074d2eb57614d50dd5170d37488ff2196a83a2fbaef1b86bc62edbd5290f43132c00b2f3feee1c06717981ddcf1556a368ea88e0ed7e0917d701a94ff2094b41fd5095ee272d8e0dae2ee7650a04d96be40757148a898f64208cbfc043be8f4e78ed5412b9a3d91a777d7ffc8fa796977d83f8747557f7c2ecb1b55fc3ea284aa27e0266801ac81059c18e16b5403cd6a72e8ebed73f27cceba4f1acceae1bef11fdb318866a782eb7ac19600a29eb2526aaf66b2f2c19aebfb169b204ddea661bdb2496dd823f08fe40077c9fc0789e6b255fdf6f504c66dd5788a4cb0fb4b1cae352df34cf3916d124a7f654e6030d923dee25df8bb6ce458e8bba4013fa4a0753c0b468b7c72452a860b8530d332783d11a8dd68841893531fde317fffd7c48cf687d5a05281afb3feba15b0f7a4a93698c66bd046e45c1d45caf4bb242d757935cad8488650dc17c4121cd0a9a26482d62c2d8fa932d55d35bf2740b0f1fdbe7ac2cbd1a7603a8331f04d4514446be3dc504cc93bd9077b19049c7eb8caed8bc1b0b0d72db095a628fda749529e7e816ace5b180d4e1c4976f694e0753b94a26359535cbe525e672ba84b2828e9591ff60308d8269248299d7ce9a3167e391934ff6f9a9c20cb02b80acfd6c2fb601b493bdcf85c28279b3b4ff93701ca5e7c5769b08e6d7087a59ea9816c5d741e423901a061006ba0bcbb3d3e52efa26ae49010eac9aaf13d7c55acff3396b5e084135ed1a839d77aa5b2721adf8f82dd98fbaf497fe7c0cbac63bfa36f446fc0eac7e38904f6341e1492c908ea61b9aa45fc34c535f410a4aa69e11e6bf8db6f7c76a33ed651dbb031dd428abc9caaf72d3b11a5c4d90037aacf2ef9c52e3bb4c090181992129221a6d4f51aa2372e69135d49007397508ea07a57ad9f8d6a6cde315d1a3508867d8b981785965a935741b93fc077452283c34cb8f42bef83f16b50da40b9f7ad1284b86097f4282f311c8e411a1cefe1fe6884b6ad897c5a94e675537a7c72370316c5e81dc79ea6c02ab3f7f5db8d717e1b65dbf02a7e9952306d04bbe149ec0693d4056c54e314a3b1df47bf5e0522648282f42a97a0795bf818a7a5e6b23d809d2b50a22a938d1e6ca54a34f88ee030dec4280569f0c9de1b732b2abe849adc8d3d17c11c614b265f73dc91be83505d75421d8c36a800f9f36974aa23421ca49d4aef71609abc285a1a388fb32550df6ce80be66da801a4e7480d46ea0e5e9d99502f36262d9719d80a9e170d9041ae34238c1c869f7610cd75854b2d3ea32cb7b66688a40f32cfa8d3d810ab1a31e2a643bbb8a85309ee6719083301e02526877c00405e226642024902e68375d1756d87687fbbc344cbbf06fa33e6478df5777c0b315a6a12e78336ff36d99b4221d27f8f2fbe3120e79e072e4ccaf0bd39b96e0ad8adfbe35c8d8097e305a96278800c4551a5c5bb02ab81061b26bc39503fc116a4908b1c1bf3f4a376d504108d514ea4d9e7d05026332025cffba8052964a7f5483be52dd292d5ce87dba2810805b9afb131a08e22e92592637e9544cde42f374c6d19b68e40f55dfa0a3ae27a7731fb7ea0301e0afb15b1767576a8719a646806b834b16b9144d0f076e84232b782ed85f1606d4888008f51e9a11dd1a059e3d32195ce48e2f89484e51c713fd55ebe479825bbabbe4afc094fa4fa57cf797a7f2e68cfc1bbce7d6e16a0210a344b5d40ad3a696a7970363d758038e00d1daaf59ac26cebc9aa26cd5b70a08647d2b39f58ff5f90f97b7dd29a040695ef041b56e7b0ea2e11cd58fcdf9f913e24dd5bc513a9cbd5bdcf2fd9f2390dc816af21190b2ae286406c947c5e9230d1d620b0d6da9d45d5321959f1f1a68d3132d15a6d63c9db114544d286f36730dd550811f27bb81fa9d960323679af5998ff2ca35bb5bab129256929401b5ce06cf4a08a1283dbf1a17ffb32f605c7f867d59c984c19e8ee71745381ec3b1121c9ac918ddf281beca7e65b0a02edc0e093495dc39438b90b578952375c5c8282405c3591ae8b05087da8b63d02892949381a53538020303dccad2c8f354f7ee06f6be0bbc8a9e9ac94b62d7f17cca095ecdfb8d524e589d2526000552540a4517f20bf09584b3188f854be98e36c52df7ad7f2caac301238c6774d439069f6d0c4cffc8ff3314a5edac8b508dfd80c4344d62c58b20097c7f09c884a692aebf207a3f5d4f24dbc711adff884b4476692254bd1c65468ba9af6f09618269385513ef067edadd01488fd08961a820692dfa7c146fc564bee31298e99e70facf472ef5edd30ceccb6509bb6c8b36e0d6aa7caf7ae13d39b03da9de6fb75658c8c60fd102e741097da090dc4fbeebaaf20e09986fba5c2b0688563867ce694584f30aa5549e41a864dc76f9cb46e16965299daeb27097f37c9504b359225090e1cd2119a9239c21218bc5e41527be9e0af64fba84eef0d9ae42eac126a953d6266f03f936235b0a41aa1e90fa49144d65ace190e43c4bfac37f90b4ce7d2a7ae2e80cf3441b45c83689d54c8ea678b55f0371c606c92542a47ca79cabb2a848b4c328a8931ecba586c106bf80eedd0ebbf5513dacea7c721e55d87eb9e9fb2790301c5692614189de796e649f4b1cbe4bdb591ed37bdb60b2888b6dc97b317b90471c50b681838b84b418f86521a3bfa440f93cc750cbd018dee3af17f17dcf8839138e8ce7c1cdf4ea2e6a2d4c73306e3eafc10e1346d1f0d3207a0163319744e82a6cac3587e37bf0b715782e3857d1d33f9b7dae89d768d6099d67b4a8088fb9201d407146252d0c21eafd9064058b57bbef66a660600f7259af3daa9ae54f5917a25cbc22b509da7bcf61d7893916b49102547a36918dd295b364688e24a9f52236962b6d3c4d7f4c720884a66e17711a4c65980b32686e75c2aacf501218b92aa76ea1f17a36d4f4fa62390ae64055246707dd34102b2d669654060b47fd1520b9a97fec0e35191df2192fc484f18d9f1d033b1edd819c76c25387a53e41a2126555fd09f07ebc07017e914cca3ed428574d212d8972223c84850c8d6d4438127d41335492ab521fe12eaccc57d86f489b5986b47d556aa913fa797148c18516391060f38dc28602f6ba7264576da966e1e3bc223586feb02f4d7a46bc6c4426fb3c0a66226ef05c0fc7150e1f93cb7eccf3fc210ba79efdea61948d8d84762f1704114e8d214f766af5050c66c9f4c3650fb0f7f5e703cbb8a20f39cc39481cf7293fb5e42c3ec7f0bd54c722edc383368d99902a3789bb4d2cc813aad2ce045e4621b428b00a31fda45261c00b2d57021a3788742aa01e34669b39aff91507bb0f8c1bcef1a13c62df9de3e3b5321ce04d5d6a649b9146efa63dad75030003d368675c759adbb6257d93437c392fdd59014a8d36363b4465dca69eeb847c242b138e08fca95c703ca16fd5013a086a05b8da3d753ef76f973289a9040c11277d1d29bd91cc0cccd5e3dda88dc9b1da4e3ec1b9d3f19c4f3420d53c704a540d0fca299400df78af938387cd3de12006ee1490c43af52c6d41abf122d801fc1146380d3eb0b489b79b2837c4a32cc7819dab37fe77e61bc3c66d1740a3049413c249513975b8f2c14e2b4ba4dc0e100566cdd44216c3246d213e3b0794142a2fc271d302393b79f5537ab4b6e773c9bebb59d8905236d4ca9d9a4cd2261ace13bdfca0349619b573bf3d9d9472d6120011fbb42805e95f792ef416c40c48d12b7e6c243326b90b87565053d9af997addc85fab2342cbc33a4ab48e9e9d6e6c03ce103989b43389c03fb4372abc67b4b5a7f8e969f06714da30e6e2fd4abc970263d9361ef213f3895ccad00db689edf8953713689ff55a3d88857df96418f80f3b7b782cccfa70134468babdb4365264d3ee33520b528a69370b28b581c4ba1039bd7b1d3ea4092dcd8ba51d1dcf66b0e9c5f7bd65abbf46ec03a2c11ab4b313cc1555bf0198086250dab79aa284be53de9e97d8f77b10076d37fd2586fc0801aa92278061d4d74a4c2327bb0e9fb60054497f2069f3882111851e3bd98e111e8a74585acc65ffad0cc8a2f9636ada525e96aeb41555ec6d95cbc35c7f0b1126febf61577d397dfff012a8a2a2ba0bbc3e346bff4517ffb82bc00218f34a0524275f846dd37a83e31de414c7567d37eae0f205ad5b609b46fd3f15882aa39625ef68e343fe1a24d2afa1181036ada5b05326d9574ed1ac5b80afc7b4cc62b326f5b2dccbbf98d6035c4513c0035ee16e43d1103c8cc9c99854e67097385ab41d08e8e1679f1ac29a79ddf5f048e2fd4af504e440b8426a8701c50d3b75066325635a23c653a00d0cbdcf49e161630d2fc898097c0659ee6bc0b492436e179e126c48725bd68d0a137e650ce703d22705efb8b1b34c5541ad603758ab07b00c155cd691a5170fe43ecfd81acd5d83b73c43d34e7106ea5822943243c8dfb0aa01d8291a2c85f4f3736720a9a4a9fef7245e315b7f056877c2f0784f83c863d90f5a0e575230ef44d9eac80fcd8563f88fc421b32a71fd3bd992ab7040be637d1b7b96a48b6cc3e46a8f412726d81a0f71dda6f61e541015f334561f5ea8f5f50dffef49fc3bb61d6365d4f0656c4acaed1b22ef0f605a3692cf6b2c6409e7262fa5919b268d24cd518c7b0b0bfce0f52cff3430e3acc673b9bceef76dd8ca3712a3c9e0ec13298a1e14bc2c16552c1b2faf1738abb3f14310081c9a295bf210149ce61ab0005ba7b316fe0cc06fd183bffa67c9c2834087c4fce89f944d0052124c65c3174dfd325c017ac48b41beab336f002d852ded401311660139a3b2c811e80b92940a0645dbd238b5986f97c7db3f1d0d0f993227f8f228bf5185075e0d5ab73b10fe7baa0c11c493ee14857248fae31a5584856b343f37a12d52f2182c5146c72d2bf02051af5c1caa8422567fd95f517db9f10f39fc5708e54cc0701cbe6f4556b88bd2551025890388336b1ca7eea6384f5acffaccf30d6efe19c90798b7950ca9d224e0260b9e829c67e369a3516c71cbe7a61d6298d191c90e8d06016db8334d019006c5b96eb38758460acf69fc48db44c032ba9ab6ea87bd59041861986a2f2e00bc684b4d8fda3768075b515de0051cdffb880250b6a221f116f53c7d4176905f62466baf88878854e0362e08f6538c7d8cb15a831f7137db017037ad159d59e1d6c4d17e049240f95ebd7a1375b5a4180a57139fd3b5fd461911925edcb0c0f56ab046085a31edc068b9972e98847326d4e1deb6543879941a4c98a2773587830b03ff781ee11ee55c695af8b876740c8d9cea385c57026e211c03c6f56c393d5685c954b8f15c8c9029a742028af63e6740ef31f528fbf71859b1ae2adbd7e2c1dfbd021fca416afb91d90d39217329217bd19a1f594f980216a12c7b64900752db10e316a778868deda29979ddd21ea57ac38d5a1452e218fd6ad620ffba6ffb5167c3d4f7d0e032fa9a7fb13340190ef68792ed8d909f0902ef3cb391891ee86d04d881f95b67111ab6aec854e716aec6839c7c6ee6e02a64965300938e463bd80f81148e2866d598e6e843e9d4358e602b22660aa6060e9117b91aa7932002ccec550e9e7c58f74c39313db7a2c66bbdc21d53af0e3b246b2fe385873352f19df33b58771673063ca079315826db3d6d2b14c6c7472718a9605d5fea60dc7f671dbd7055fb2e6ee7ca651aadf1482295c13921ce1b3e26818e16427bb2bbd1045da1b8e49e4e3e7d9817026aa5d2f03157626b17a9532da63e4b959b21dd12829c5bde5822056de6e644fc2eacd2d993609bf22b78a50cc023a2f5df4f0ba7448064ce2ec6f5052631c99eab0c66dc208848840733f00e939dd6a730151fe93f7b22655f8c9c443658ba97d6a947809b32ed5510928a2cddc65f807eeaf756acea3fa946c9e05ecc26c68649567d47d66a701f552430214b8173ac6ff586f2754c73f34e024ba6ef907c6e2ec34d83d619980ff4b8e72ce1fb2401f60ba3dbb4d7320768f202bc80968a811ffbe41ee2c0e7e4e9f3071ff4e4f04c8de236dba3638547d57c459c8bdca8616a4a59761480700cadb369218200378fb4db8b25b0d7529f083090a7aa8fb2bf9d1da3e4e66fbf50e262080da0864fbc694139cd3b310bcf206d85b3b97f67cfc0e4455324b7ac1e3c1a711ef87c6b980c5c8cb471c6ac764008f859f06528ca70564c190dd411d45b2bf60a63b2fbf8e5bc7d4f60cf76822c199afeabac46ec638b571b6ffbff2eb203c72de58eb4634937f720fb70c8c37d9f18bdf0e4d18cdd4783aaef321bfd8943211f50104dc0501cfc8e9e2dc443c90290c063028dcf39fae0896f6359bf98c82d00aa9179e566810bf417ad5ee32a551ae96f0870b13dda21cdcbdc7563b320262ee77f55be8f856f5716082c82d13514e14795676ab6f46605b6f3935c3c740903761c60b3dcab05864ec30b350b993d26b3927579587bb91ecc1a9a4090fd832fe502368d2c16d5a190ef0ee84997e5a9878c73aa705283cdebb67a54298da22b886c69c65d60a6dd111d046c1b1c49be26d2e422beca6839024819dbfccf15027203792331b6a713413bd7d6ab58b6533367bae966942128d1b12eb87fb49c1b3ad1a1a34a37ec9e228ec871ece7a964aa11795bd40d1929260abf226aa97a", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed0000000000000000000000000000000020efbc1b4676f6018902362ead77e27d0000000000000000000000000000000060ec24b679943ff559895776f6f11c4429012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022aadc7250d71617ec5c2f852c2e49ed3180111ef6b130ece8b3504a7e2c3d0372c30f9e94afea47a8631132310e6d72615ccd3227fba92d2ae829172e7bf1c2d089943510caae41bc6adc5e284e6194b797027236225a98865400f364685fd6609bd127e0b1c620665f422cca7d6dbde8d84465e9268825c7e12feeaf3590216000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } + }, + "test_exit_code": { + "crisp": 0, + "folded_export": 0, + "enclave_contracts": 0 + } +} diff --git a/circuits/benchmarks/results_secure_agg/integration_summary.json b/circuits/benchmarks/results_secure_agg/integration_summary.json new file mode 100644 index 000000000..e3c16fd88 --- /dev/null +++ b/circuits/benchmarks/results_secure_agg/integration_summary.json @@ -0,0 +1,244 @@ +{ + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "secure", + "bfv_preset_subdir": "secure-8192", + "bfv_preset": "SecureThreshold8192", + "lambda": 60, + "proof_aggregation_enabled": true, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": true, + "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "multithread": { + "rayon_threads": 13, + "max_simultaneous_rayon_tasks": 13, + "cores_available": 14 + }, + "operation_timings": [ + { + "name": "CalculateDecryptionKey", + "avg_seconds": 0.612713375, + "runs": 3, + "total_seconds": 1.838140126 + }, + { + "name": "CalculateDecryptionShare", + "avg_seconds": 2.183483124, + "runs": 3, + "total_seconds": 6.550449374 + }, + { + "name": "CalculateThresholdDecryption", + "avg_seconds": 1.943779167, + "runs": 1, + "total_seconds": 1.943779167 + }, + { + "name": "GenEsiSss", + "avg_seconds": 0.804676139, + "runs": 3, + "total_seconds": 2.414028417 + }, + { + "name": "GenPkShareAndSkSss", + "avg_seconds": 1.468442666, + "runs": 3, + "total_seconds": 4.405328 + }, + { + "name": "NodeDkgFold/c2ab_fold", + "avg_seconds": 7.19246175, + "runs": 3, + "total_seconds": 21.577385251 + }, + { + "name": "NodeDkgFold/c3a_fold", + "avg_seconds": 51.025217958, + "runs": 3, + "total_seconds": 153.075653875 + }, + { + "name": "NodeDkgFold/c3ab_fold", + "avg_seconds": 6.830379416, + "runs": 3, + "total_seconds": 20.49113825 + }, + { + "name": "NodeDkgFold/c3b_fold", + "avg_seconds": 55.386840874, + "runs": 3, + "total_seconds": 166.160522624 + }, + { + "name": "NodeDkgFold/c4ab_fold", + "avg_seconds": 8.455168041, + "runs": 3, + "total_seconds": 25.365504124 + }, + { + "name": "NodeDkgFold/node_fold", + "avg_seconds": 16.317384, + "runs": 3, + "total_seconds": 48.952152 + }, + { + "name": "ZkDecryptedSharesAggregation", + "avg_seconds": 19.080945417, + "runs": 1, + "total_seconds": 19.080945417 + }, + { + "name": "ZkDecryptionAggregation", + "avg_seconds": 50.556414917, + "runs": 1, + "total_seconds": 50.556414917 + }, + { + "name": "ZkDkgAggregation", + "avg_seconds": 21.026504167, + "runs": 1, + "total_seconds": 21.026504167 + }, + { + "name": "ZkDkgShareDecryption", + "avg_seconds": 52.175752756, + "runs": 6, + "total_seconds": 313.054516541 + }, + { + "name": "ZkNodeDkgFold", + "avg_seconds": 145.216893416, + "runs": 3, + "total_seconds": 435.65068025 + }, + { + "name": "ZkPkAggregation", + "avg_seconds": 105.563541209, + "runs": 1, + "total_seconds": 105.563541209 + }, + { + "name": "ZkPkBfv", + "avg_seconds": 5.966218292, + "runs": 3, + "total_seconds": 17.898654876 + }, + { + "name": "ZkPkGeneration", + "avg_seconds": 379.488959389, + "runs": 3, + "total_seconds": 1138.466878167 + }, + { + "name": "ZkShareComputation", + "avg_seconds": 101.477507611, + "runs": 6, + "total_seconds": 608.865045668 + }, + { + "name": "ZkShareEncryption", + "avg_seconds": 288.672301658, + "runs": 36, + "total_seconds": 10392.202859709 + }, + { + "name": "ZkThresholdShareDecryption", + "avg_seconds": 305.775330152, + "runs": 3, + "total_seconds": 917.325990458 + }, + { + "name": "ZkVerifyShareDecryptionProofs", + "avg_seconds": 0.114692638, + "runs": 3, + "total_seconds": 0.344077916 + }, + { + "name": "ZkVerifyShareProofs", + "avg_seconds": 0.34096735, + "runs": 5, + "total_seconds": 1.704836751 + } + ], + "operation_timings_total_seconds": 14474.515027254, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { + "label": "Starting trbfv actor test", + "seconds": 0e-9, + "metric": "wall_clock" + }, + { + "label": "Setup completed", + "seconds": 3.246619875, + "metric": "wall_clock" + }, + { + "label": "Committee Setup Completed", + "seconds": 20.239166083, + "metric": "wall_clock" + }, + { + "label": "Committee Finalization Complete", + "seconds": 0.004966542, + "metric": "wall_clock" + }, + { + "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + "seconds": 157.906869, + "metric": "wall_clock" + }, + { + "label": "ThresholdShares -> PublicKeyAggregated", + "seconds": 1350.540072166, + "metric": "wall_clock" + }, + { + "label": "E3Request -> PublicKeyAggregated", + "seconds": 1357.853179958, + "metric": "wall_clock" + }, + { + "label": "Application CT Gen", + "seconds": 7.889468042, + "metric": "wall_clock" + }, + { + "label": "Running FHE Application", + "seconds": 0.0710175, + "metric": "wall_clock" + }, + { + "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + "seconds": 70.07983, + "metric": "wall_clock" + }, + { + "label": "Ciphertext published -> PlaintextAggregated", + "seconds": 389.344160916, + "metric": "wall_clock" + }, + { + "label": "Entire Test", + "seconds": 1778.650297542, + "metric": "wall_clock" + } + ], + "folded_artifacts": { + "dkg_aggregator": { + "proof_hex": "0x00000000000000000000000000000000000000000000000be4031062af5469ba000000000000000000000000000000000000000000000000c2b34be503df778500000000000000000000000000000000000000000000000d7e8ac169d44cd819000000000000000000000000000000000000000000000000000226e9f937dc8400000000000000000000000000000000000000000000000eb2c7b5a6fbbfe202000000000000000000000000000000000000000000000007e63ce51619b2b03a00000000000000000000000000000000000000000000000cd2bc066f5bfb7eb0000000000000000000000000000000000000000000000000000180a275d95616000000000000000000000000000000000000000000000001588f71ba712f91cf00000000000000000000000000000000000000000000000f5e274bc9e3112d9700000000000000000000000000000000000000000000000ce2900e6f8a2b3ea70000000000000000000000000000000000000000000000000001d450a0feb34a00000000000000000000000000000000000000000000000893eef95a46af9b6700000000000000000000000000000000000000000000000610ea1690987e94780000000000000000000000000000000000000000000000026773ee1ccae29e350000000000000000000000000000000000000000000000000000f7de5dd648ef04d5fc40bfcb431dfd8b2fe77b6946e66eb7a8691ab31b9ad300d29366030dc721ad9f08fd9c1205bb5a208829de6f760b8cfa7311a3d4e9012cc8357b8d6cb9099a4a7906202912663409413b8f95db686885add4bec427fda8a078948d4d5628aa9359e286cfd9e938616af59b08fe7e0ddb1670ede6046a1c0c58e62575dd064b6290f765e9ffe9947c9677e07f369d130a04e51b428ebe31ee0728043fbd0202992904bb4d2cb55f641534bdab7fbf513e59fc0fd094a50116faf966cd71266144d996fb80a1d67ebfc31c554736779b58eabe68a9b77e288d8de098bb6825d67e8f8af229c06cda9a9e53c6dbdb50d895d4a469a46daa56f6b37a7895131925d6f35fcc62ab791e2d95d20ec944f9f2a744da733c1d2ff4ec7742d7ccb809c38965d7317c3eae99030b673c6b7311bd610393138b0b54fdacef3f7dcbf41d5858342b0f9a0496cf06e00856a1c613c46164b150eac569b07e31bdb1f5c50292ba9b7110cc200cba66dacb3700ca2b93cdfbcd99547807cad9a6778416780c0b613383c0e7b7687df425fbeee700961f6e6f8f1cd8edbe2ded521cadd5972e2b63224fd73010db4b74ed0f3c7fbec0e1c78c7c30ae38e3fc7d58c8096e64048dfb1400bb0cb58ea4adae69d667fe92b899b921a5dd31783f26145dee63242ad083e6fdcc90989d2fdf57b4c9ec187315d9e8cb8ed50584fca7fdae08fbfd2d34411db2b5c57b6991d03f887123ab0fd0a5c450c48e39357a053112c62f992f8176cf1c648f258750d0a47d97212b450cbab15c3e0060d71fec9b39a7bf96280ba3ebb516bbb4f3c8f180026ba77e7a1fa6a64481c98c5b6f93781c69be3a23ad733505ea5bb254d533dd64fa7a8634a18f0b51523739e32a37ca9e9919a106dde1b8992f2fabfc973b5f38d7d3b1813220d03c4f3dc1a094dd72bc5b2b5b06cabe3e4f4af547834694b5aee1c436ac8bdbb8a84625ad4a9cff3ce92b824416d14176d0fca6bc452e100b2415aeceec371d150c69c78d1af5bf854d28fe8d0e10789e44f8e549421d9e46bc48990aa34a13b16c579e6c8953262c8ec9848006371b9b26485ca867806e2305043527f33c33b2e96c25acdcc69fcb04005ff52d17668a477bcdfb06db62e47445cda78b5cb6094f99cc644ae2f97cb48587af2403fd78d171398769e6386a60ec0c1aeb0a80bcb74f33f334387a65d27e233e0a989c1bf2cb6b7f44ac3665aa5c0b0f13e6fc6653d22c64e2493b2a2c74bbb704e181e8b3a2053a43b7556861d49fc25383c3c7cca711c753082fe51d939e6d1fb4f93e8b2e2d170363468586d5c3077658532578c9a139e0c0212ed1828504181b7790dc3cf4b61e7f986479673f74fc9f5881065cbf09321ad9d4851c9f070b98da60896bd3e085d80a970ec25538045f990b396ae1344ecea124ceb934a110b05412c6cafe9c6c7aba2cf8bb0a5b6220ac3ea6c2fcb41396ce23ae5d12ec15fadb44fd24e645726cb35b80b4f814500fe91de9f610fa9c2fa48a75737ac721329c7803475ba0ac9b7a68162ba81c2100c1b1ddbcb8da202325304f861d7a0ed2dd1b91db742084da868504df566366ce735028525a0ee60036b35217dc2f08e23dccc9ab6f4868ff67f443a726b1f587f3ffcdad2956c5deec225b1d3dd3305a4677be71c9cf4a5edf2550397caeeefb6a7ac93e5b337df2ee7675bfcfc917c1c596a49a1ab8405afff2f51b986aa5a28b989dda03fe2a0140506c8a2e351951c408a0e5dbe19d8a0ce4722ec040547dc71cf953586c0f10fcc03b4f798c27184bd60837efef5fea31825f46e8cd3e5c885724ef62cb95aff70db60b7fe40f7ee3db53fe24b1c8e0758b724eef95d0a9cb915d6dc2bff7c8288b372fa8e516442a51a759f689d8ee3b50872c5b37129226d01d779f3dd5d915ab340d104a26f02a95935c07ab2c35fcb8660e460e42dea6867fcd34b8ebdc03d63d5750a21f121eb8398a7144cdab0e4b91dc7c98daf03f94227dc1b377932541c18a87c8152b0de3cc7d40cf251e332a5f3e0f4466bc5c556e8dbc645d0510566fbeaa3327a3da254b540f97f63be9bcbffbc55f3c2a9afc8800a89ec392697488e58d7c0a7d29df822188a9beb8800d77652fe24220c533e36422904896121cf80ac4f91947f3288b60df0b73270437627f79edc6c4a50a290ab04ec8a1cd4bb653c08507dc3ab933d108a7d35073b6e1fe66bbfbd2da7617e217fcbfd7756bebcb3c931a94932570b9f249d5cbb1d156eaef1dc3d8d406a36389fa543559c76601b4f31bfa3d064c38230b516295b9b132223a8878b42d7bae00e11b6c0d0de84762150ef357d10f3456b0624ead2f3d5466a33e94a19ce0f404ceede01daae1f9cd4704359a359819d3fd9f33fa1a27725840d32e212f8772e10db8e7fb867a0e73dd2087bf3e7673fc1438352bf3800d80741a3442c52246c7e383e3d1e4b475493c1a54caaf8109e954d3d68caebb90ebe3be233e7bd0ac83ab9dfd98a910b8995b2747c909a5152de94ba4499730f822670d8d8460b5b2bea686e152345934d630024a6b6a55a8c92fa5ba32b22ecccd61111fd1328df401dfb9b1bb40ebf7211a28dfcc09d7227a314333fe16e87c0d145c6b86e1131d1bcc2c9c282a10c97e69053812f28048a2e8592312619cad05387d764af40c7f606f9932d21cc5c8a4512986aa893611720a415a1089ec6c94ee994b5994f4333e64acdc6b726afa58d229641c15cfe2ad9aa6b07b0c3d714492f0614a36691c27dbed38c4e4786c975b10d51a3badaea2f5953f9d8fa4b89791c4810256fdcf8c0a5b0b1d04f5e1765b08684e7e1e5be7844742c279a3a6b721ec4abb18e503c13c002f26bbc4ed591c2a48fe38eb62f4ebe639655798ff0cd6b3593c45c254cbfb70fe3b4c57ec41322ccebbb8fe78c3cd6e3afcb83caafad2dcbccf1d479f8487d34498f0d7a29c9d2f4faa41939be6979721858c284db8748291f1f59e79cd2076fc2028a838fd4d136c2c53a95dba376d15bb809228ede99b62ff234846e8d79b1630525424939d0eb735ade9063bb789a10213b88a646377cc172d2362c124bc16316a9da602f112052bae671daa4814c3c557e4b30e85d6631ddd7899949657411440439eab17241f28238e18a8423428268bb2d8ac2f1bba7c7501504f7d74c838fbfbf6c4c32f7a77a45907a63f88f909a829e1af089a0a9861f364977984ff8648c17d280e0c5c529d0498a7d069bbec58923c0a1491306b5e2c4a2e89f27daa4649093dd41d7d037bb2ab62fdfbf67e86982631467ac4d0c0d803b0b5b4528947497915f82b11290cf8c218c0d82a12ed344569aa01de3860fb9ecbdd2f17f5c82a21a3bf021257ff2dd7acfb3f390b41708a64740348fc4ec134cfccddeac260e3057a430f1db0beb66364702924b80fea98e44501d8eb7e4403d76a08d1c572741bf5060794415f28e36586ebeef7f511eda761acd12ff8aeb5ca3b58b4c03b44e9ce372309b2b9f3b8663df86089972bb354c48d7d38a2273c466a91cd8dd2d71fccbf1ac7456664cd6279daa94c646cc9ba17dea5c65590eb5531e1fdf991f2e2f2f415469909610a37f62d9ee59a674f81d8ebe78336daa033101f936dd7a6d954aa0fc521283d08ab601ac6cbefe31b1dacb8bc79967f81103f843f889cc0fa0a3d2051398cb52abdf4bcddd3043e6eaea0f939cc345087bcf77b49a2c19f4ab6db2205c0f3465bc498d1b1f9f2ee07875504fdb9f7c1bf1231dcd49ec059eb71d22f5b76bb0f701a665afb6b703fa79852e3c6d27371d14dabc4eefd3072f113fb0390668382343279faba659117b644b34a51c88a73067a046ab7701648068e810bcdcc91081e9e8680a197c17e065dfb2e5d4e5d616fbebb6b1df02ae4ea251425c5822c46f8036681a227abdf7fa7ed84e6e15257fdcd0830dc5648fe30e2f417aa682b0e494c543afe329b4adae6a53b3fe04ed76f4de0d03c8a4d63cfcc94237749ff882b542700127b33317658b312073746c450d3ea58b5ed6c1d7a767704dd753702f48e19b72915d755ebe09547183476093c2529d5e2bef589c0d0b32dd044650720e56a65795f0a7403068b34ceecf040b774135bedd43b7b2ad11b0c434c471ca600adfda1e2180858f5729e7d44556a6113aa9d208dd212f908862a637ed4aa3fcb43b38dbf481191fe72d8784ad72903fa2157577bf6dfb5271b2471f38190595c7eb3b56b8e023ba32ff699e75896bcf3bd192d1716710396ed2112caa373718b77c3f77f34ea151597b053d467b35db83defd031475220c9ec2a28b6a362886f375eadcdef88b87b3c57b84aecb429d0573f5ffb0f3624bf661704b94ccddf42d78c48a29e69d2d13d2b4e06a67ac2f2bb208636de76a1afcf08939ece469839835025f0e579b3b394a56cd4cdb125f148764a702aac39e4a01a361644723ade9134108d783d0c26031867be39da49fc71d03fbeeb48941e6416fa54ede4a2d334c937ba433ff61ca4cdd3ad87f4414eb8bbd453e3e11afc5f0a35a4b11749feb9912453aeb8c75c73e1972f9073b26cd1486d39e26d76fccd1524a7d0b0563592edfb89653413932cf5721d415a98b4e08b79ea6eaf02af5a01d2a8c3ff6244baff5d2d02ce87f6ffb18a6580caf33eee5da6c901f2ac5e8d0ca29bd454a302707be876a378d621013ead7db4e43a9d667a313a8af2fc289703c66d20b6849b4fe6e2f5fdb75b5d8fd69b8cc883ea68f7b54f0790ea3e947d19fc5927c8cbbcbd634ca05bed4049ef4eb11bf42eecd3b516212e096bc5c3d904db39dd8f4b5a4eb81a44e53bab45976e769a43c7a20b462f2aebcf97532a461e49bf694ada5220e401aa192bfc0fc9bb882e44421d3908b9edb7a4a711728922d6f34ba9c86300fbef97a31b5086731356dd51608f8ac4ad2903d09b7f68b82cdd60efca7e5f46d48d09109faff7d37f192869e76725731e44380e659aa7660f3350569c9db441fe0e242f2cbd3c5676f76944ea842c2999d68492c322f8de03b6be2eaab796d530a10a4b25bc4219545214c632cb3c90dd7e36c02a47a8b418eedda0eb2f69d1baa29b3419bb616b29e8cd39ddca7c7f4d35bb9560f7296105f32a0e70c90a955198bd14543c67fcf7fca5115b01aae2551ac66fc040315e2a5fd13d413134f0f7d3c0bd18439a56712164cca1e6fee01fc8c2cdc86d13502b3fd3737d7297939ba754e877016d2dc5939058fc184b7f58b26af308aed8a925b8a94bb711a388ee4fca28348b00e0557c50cecb8fd4544e6d6b5af6677968170521b98bffe10c997a90dd6fd119e62434550dfce1505e3ff32addeae9562305345045e29180416fd0001463ac12a9dbcad68e044954fe604f80c2718010b6286b57d3dd3799af96ce143c0d2a04b02439b7290852f4f4e7073cc8571fece212695728e28841584a2f60f315dff91376d737fe8a65a854e9cfadb3b1a63aa4025c333b7cb9e24d01f8177fa69403b0aad581496c71f25db147b1787ebdb45a25c0fe2130eef2f3c55fd9eab3a89be571e3d63ff6f03a41dc4a47cb7486ba1224fbeecf0f855f6c2e954b536e40228543831ced74904581f9356e75ded5f90d02d50debd5218243359b6f38f9aa0c38a2152cec4839ea5ba7cc647e3f17ad4e114ae3ec7066e1c751636b31a4eec65f271286fe2f35ed724fde80b37d2181f42c7492240793e97702e934b62faeb1de66370e6110a989b8cffd44ad68cc6653263f9b9c1ed40bd3deaa1d80e8cb20512d3591e1611bf64a4c06abd7defaa4b52c1c810c85664e8ed7fb89feb7343c01cd638c0a5f63d119bba094ec411c00510aafd0e3a82da29ffbef328a18990ff941a97a9177a8bccfe5e60f17690a774f1a152d6d2524bd5edb7ed8e30d6d410ef11c62113d3bd1cd861f101b1d0026ab140202b13c0c21d5cae1537aa9ce88b574fb16e00715ff82ddeaded5afe48b6c285674603bf9ceb6f57ba50c181cd716244605828d74c7a949d227a698aa05fe15af20dacf7ca7811b7df2670cb8b7f91c32340afb13c6d1d6531e0475c2bbf81402cc09548d27596066c98d5cd270f87585e0a534947697ccf2350d5e773a3014159212f641b1722657797ca6189aa32918fe5ef8835f1d3dbd85f4e6bd673f1aa0132c34791f2d2cfb24cc7bc3f7f43a9b8211d66decf494ff79ae8e91c72d10a3ffb79b85a84e90e572ba5acbedb8754246c0531e5b12d492043779cc8d0e0713f4a81c960a8e60354a797401b9ad06d37294c43a5ab62a566d8dc6e859fe0b3e53294dfa5d2645c1db407f9996a33339bee0df57cf4ed5f822e2b3a63ae41fcd7e4e0e2a10ef9a1e4786a6aa63c6bfcf6872b83592105adb71786c056c991c79fe5189b53521f1b3d2850baceabb6cd9f12adfbfaa96183fa43e12ca72b32e943df706dbfdebd57135e75056f0be276d51e190da31143d741836e827aa22097ebb616cb6cf4adf5e035ab8d35e5442aab3330717c0ab5f77902d03cc98d42d2b49e29ec28b39ecb70f9ad99edd8f1f1787df8dcb66fde0357c2af545c56815081b036b8ddf90a266d46528db02e603ddb68c3f76fd034e64524c9bc672390b451d3b1dcc9c967f291f5ad242381aa90ecf15851606412829f6e22d6bfed703fd8cae5a40530831a19cc3991b20b6cdc4cb716244af876033e2741012a3d30e5acb082fde25051476252d6b4bbfeb1c6d9e9a3c8f3a1d277eae36e458c20409e74c7314b02800b3e5df775133e0ab515bffcd96353710ff873586a68f22ef1c84883a0bcfa37da9f3b60925016b56df268067ceb4b1ee2880ccccbe7f67ba017f79b69518a3d6938e48f4f1b91096f65cdc0f74765fd6ae811b2422e976c0039fbe249148f354b79079ef932de9f67d65334a3bc64fbc56f1d615a0e20d7b182f773b8bfa7b3769c2f32474b0560cc803d45ea7189cb72436048e96e691631f70eeb01e87c7d084152f7ec29cc7353ee482a17b1328d90d77688c08ca597a2eb6ad810846cae0b76021d37b510857a562d73698cdbdc4f54fec9050e230da2af550f98e0e00818256856d0983d75da2c79f0bcbeaeadca8483d0b0ad2315f0f3742e9e70f3abbdefed8d6cdd306a678952bc1bbb0dec66f73ceae932922ca0af9187ce31f3a8dba71ba83dd28e38263f4cf8203e52c0277049672b615ec5703852197e8f0f8fa6230f2b734edf0d6e6e72ddc23b9b59da7f7f62695408329279532a9f9aa192356967ef60667020bab4073998a31581b5aa2769e5dc4708329da847757780bde50232030fdf6b1807fb98193b7e3cacaf25a0d7c4799bf9b1d48e98daf16badf314d580c73d32693de4d749a2c748dcd90d7c5c43b854e7b1fe6fd7209d4b47d16eb531f7cbde3a17ebb240b12fb467d0c2d1e873f4481a40c930a7d752cb170079ece62a0859060f81062f0fe6be060cb922e3011addc060a5d3928b2fe65fde80aece1064a6a3cc5da0c9f3a7e98e2d77640894b2a18c2234fe7beeb55b241a9cea1abd795d4d3b1173d4789db2a0eba90a09078b7b29a2327ca87ba607e5e2ce9823319fa10845bbea94dd7c8601d050a2d9c230342c007091c45a7604d4126bd5ba268bc6b96bacf6eebca8e357f96dc9196cfbf1262208bba9cc8c4468f9e020ab07a534bbfb4e19e9bf2d1dd2fa7fa46a807c5d2191998b29eb8c1a2cc49c5d89321fcb84a58d017fbe187f8258450d6743390da2f2e330329d0cd738eb9accb2757aa2ced21ae3be0641306c8d05e7b945717f3a22467da32307db061fdb0f1b7ddf119f9d4c70dee698b9851a5f7afe39ad0517b0f30fb81e286136bd256626fefe97ef6865c712bc0ea64b3c05d5c2671bc09c22329005bbe9b8cdbc6ebab0727d1b5f2be95500317aab579466744c67137c18107dcb8773498048d26c2ad069820b1be6997bd7d0fd2a9efc58321278a3815ee1290bc714c460163c534f2a67a207b72771b9f75b5e09772cedcc841485c3f761c34f787a0fd0cfe3a349d19616f1c8f2f3408b79eab7402edb8642e4fab97e81b84f46a8a7afa6dda8309ff50c780133c4bfe41391118ac47da47f6c64c65310f466d93d50624cc5ae498e137766c29992e4f540831934b104f34317917ec361d725b9c2122fd72318be3b95e1c228bbc5ced08aa9178d4c09f1c3fdce06b3d0ad6aa3ed19456e163d727406b9e1e12359a8e1ca458affba8795cb626ee243e056c563d07018c4d5576310ab32e0ad890618d5bd0b4c340df49a3324601e0c61669fa62830383f3ead58b2360986baa7084f38bf54e4ec380d9ca67fd9563b61c42ef088fad28f92bcbfae2603a3bbe0e3b3ebea40bdf7f0d1b00a997ff5e1b15e487a2745b2ad061b89772c2caca6942f8ea111d61349cf64adabd842845c7037ac83db56f677018835f0adec97a2e23d615a9affb8c114ae30474af028d862d169ab4add11ac133a9f0f86ed72ebe6784249b62cec4dd70d66d842a7004271fc4a532858f1392bc3f447beea906c3ad8a2600c296e80c72501f1ce9d0a3652f1b51b6707c40d8e4b03a907d3b61b8d77a6b28b0361d25f5fa852bedd4308f0d1bd4617a908f95dd7d4d61f02b6299c2efa5daedf8b276ffc60fe6ed7c74f92aa265ddbc2997e483ab7d72020b7bd63088c46ca4387deacc6af4becd1d514928a4e52bbd565a9ff3ea24c8cc32653327868e836d1c6d70d9f2dfb85f8ad71b2937c47ef2e32b728d43d5fde76a027353ff5715db2825010ee75c02f650b0d31dd46ebca018f446acbdf62a40fd7f462ce908ea79b56992be5ee319cfe0c5120dceebbf0577e73fb6f9522ac9291b1aa478ace1f77bd3c27ba8dbae11ac01ef1e311c049104ff89d1d03a814aea07d10571bb6c3a1a03d199c166c9d89c8a6f0d8817b59b5f96711d9a76253cdcd4780752bdfad35ab53f139213516a0f590206ab60816a58c3d37c7289c38e5451785161a99fdf0a8ed655253225d2a472a4194c4efb32d4241593cd470a69d14bce7e77d62b3516377b7714d7cc72c528dc2147d770e033a46e87c8dcc59d1f80ac013a907d88abcb8ead6dcb38b0a316d11b55f328f6419cdbce83f354d7c6c21ced11005f2e797af3ab2b493623098b0408c68bd7ee4105b99ac2582ac6934481c35276e0e97f4282d52de0a9c551d6a11cbd2c6ac7b94d2703b8970beb49631fdf0b185ac097894c055a4f1e25b6bd0c09d60a6817d44e48213af231b0adc86ac26551a5ee1cb95a63ff0f6266d4ac6d08247cafa25b0bf66ac26718882c81826140e45d0ed00a923d0392d7b94d5bfc1d3363dc3370c556c4deb3aaf1f6062dfdce1a64ffea0c70a7f3c4b596fa5d8c0d199eadb28c99f5d52c29d62d8da00274a0a7f995c4986424d1ca434c105a881c53e3433fd5850470241659b7cc079cc4c24ad05a0ca557bee7144948bdb43b178afe29a44f2990bfa8c1bc29781d110d55e6819c13cf141a870b45875abb402dff7d7b76a82e59083e99f95151f279e23ebb951eed4566d885e7266529eefd295b7597c2687529f861e6ccf0cd557dfb67ead6417b7932e5986058bffcc30d082b82b37a193b1a4707029f3537de2804a812d6163e56c3aaf22ea3a785c86b1dab7793e05c29ea6b322854a1134295ac17658626b964365cd15b4fd9ff16c027a54afc15889e3007a0348f9e7bbfe505d79a9216d794a873b4c83d5a830e5414860baa803026759fa3bcc5e37172863e6b7e4d70e344871443d0c5cab8d4b601a98111433657f2770c31c112fd57cc42c9245cb151cb1aeadd23a026e436d723a267d29760a4b339780a32c41c7f5a6510650cf324e6d688b758f2f9cebf7c19347c3881ed00a950e0a3cea706b404f45e9b1d8f608229d18ec6f55b4d2821179d341f4e781ac9094a28b5cf4c1e0c6222f49a788db9c774621ff49daa3c4200b0260ec89dc1889f9fec750ed80d6c3c2be8130ebae08ea208cb1dbcdaf0760b4412c2b2183a26cbdc043c9ac34f6b8d15c9cecb94490b951a8559b914da3e0f5a32d3350d6e7b81e3f45c78f1837ea1fcfe64e14768706b01fa1b6c4f61bb10695d7a599f27c0989a820218fe22c5676a8bd30db5390abcab432ef521b6341e7d89351ec3c66305f2c0ac6b1cf4b0ce11dc6a847e6583a96a934d20e464a6290be30d8456c1aebac52cc6a5d0d7d260f9df4bb30bd088ae6362b3eb6cd9b6049e506f33dd3ce4078afa704c361953bdf6c573203529f2a6a0070f560809722476d300779e00e14bbac5a882768ec7c26acc505ce1daa807301b883b04dba7283c5c9064142e834e6713920dd9d2d161d0b363068a731e11f14c5f792b597a0fe87b75e67c58d24d76cf08dd939e1272b756e5eae7b3c620e663f10f2935fc1ce194e5b7ef43a203a8bdaab9ca888ce83ca654d5e59270b9d210d7f64a198b0e12c66d667f7f71c4cb1314ec50ecc7d129debaa2327b182257e5648a7a27f8205433d8457aa829ff0eced68d08accbadf6f975fd31159fc4c097ae81ecb1f60f7fe9c234d7074e92b6333b0b819cecfaffd18dd2977434319c373a0e4d34ff1115c1b80a81e1a261e9369ccec0c082defa52fe8174a668d8aef64c2740e9b107f6a805616f79f4fec5900fc61792c4a71f2212f6d59f7475329eff567da947133d4f8c0c07b34ebd0db3f01aff1702e6b2c1eb8354f6c9c6dff3a4f75259da0b81d5a2a0b1047d2365ccfeee0ed9c96cf199cf703ff80c9afa6d6224d30227199193c646df631334511cb4003531d4f0eeaf19cfaa2adb9915db7011deabde265b6713610f82d9ced0fa5684bed0807b31c1b27707bb1c3c6acf4a5407ffac064015253debdefd6e0054d89a405308fbec4f9173807df2659340190744f4b5232950c2cd17188e521b81db6df3b6b1e39caf938913c13f93df8ade49a5ff4a07286c295dc83dd453eada3bfa1750fa12bef03e44f24e171a622b7212107ef4091ad7b9e66eec303955e11c51085ee170186876d7c2acbbec43835c6c97b16505f41043d8415ce73b3bf2de37811737de54a20eb9f050bdfcaf0d624f5c3dba2aae5d982d5e01e2699dbfab33228921a2876caa668275693665268fe14f1d9f127503ad77cca35b54cc3f1475ffbfb29900c23cc3b2c1512258f9bcc823263e1bd27cdc52dfd62dad4925033970d39f0d65114b0118963cf015a8d58973e76d0024ba4de3892b22978e5e439cd528bbac8ba666034052af00f9402844683839100b8c0783efaad35198945c3d98726886c596d91918427e81a222fe4426184a2070889b115fb5bf0c4a757cf56f755db917ae10525f6604cd85613dfe647dcb0507c888282ceac7b445f109811bf5f3d509d5d5455c9a9e9df1693a87ca3976074bfc8a21a1ee53baa63cca744869b825c4f57656d8a4102448ffa04b994faf0d8340fb40a86f67d1ae0898ab2ab863a763cc8ab45a58c96e02f5d2013107632608637dc8dc6a019d6af75a1ccd7b0e350d0c8e5a161ddc1c08e7c9363c41b7126a7ca47fb8a4bd3e38f0b529785cdd02109450bdd55bbcb3ed03c755337b0e1f0bf6717ca7fce898a35280edf6f2c6b47bde31b0a86ce835a0dc9c10f891ba0c7fa8d1b2c54e27d37c5adfb06ffd4c78ff6373fd724c7ce69119365cbfbb0a241de27fd2ee94b23135b9df33621272e75d9cf9560635bb1eab4c6f60f75a84009783951dca4cc3f935b6827c4ecff13f473b7c11b0f96a5be7718b24b2f0f62a5f7ed6c5161ba762869f0c8b6cc009b07cb8452ae6104fdb799767434f9ec52bf3b87f7aa35a13cfe46e2318be378b3f9bddf4ea9223442cfe05c4b18bac0e271f21fc429b31ebce90f705c31f6c19d9781feff3ecffdfc261518d454f12691da279357dfab133eb08889699f75174df8dfbbc381b48de70c80a440cb666dc0a7030f8c8ecab1c1c97d5c7f2645d60891a391208593f8d7e5aa04bdcc9d50209356eaa0695cb0e5a49b0dfd5440d958309583540c02d13b3fcb4597176c3d50c9c16dece31449cd2574a45d99fc4687374c94602102f5b46246ff60e6b37372a6809108be44bf7b89c493bf154df1373f8d9086392977ca13e2cb89de5adc219f76c71cf919bc6bda4d0fd6d0d07d4ec925b2ce2ec19e9c3c0241608419d861c096b79285ba465534064948e058a3bee97438896dbb2f4cb40c7cb0ccf41af0996f8809b176892e0c5da063c36a6930a3b17308a2c4e8dd0aaa51e72b29bf0215ca78f71143567fbf9374d7e9793e93d6a3458998b8bff08d5709f1e58f63b0736690dbb85283c9d46ed82c2d90a3bb69c84617c7c88e4c5cc9d79d66dc710262151641d45ba24f9a918510327fc2d31b426b92795ed9f090ef559cf30f1201f8d0d0f78d7898a04afc5fe83eeccc1de82945db199064057cc868e6900ce7c05617799dbef66552ad082b83f9eba55a7ae4fda9acc94e5c8ac7cf11ae65053062921b2287074e630a6f20c1fb6acb692eff24c1c9fe9a1f842b571fff806e625f4a286a5725188640abced4f491d781c8597161fd5a4bb98d90ef9826f599325be04d44fc983ffe469a70f28b4bdcd7a044abd612a4bb4ce22cd3590767d2d0f5a272080032fc298aad59b56a8c04b935c4f5f61f2c882ff010843359cc5722702cf8db27c7ebe19821f6a1fa2b45378ccd62678f2bbdba7fca7e666e3994a17792e281bab19d98bb9e4ff096b79c3af935b151cf0a796a74874ce1dac0bc11dcffd953af6e5d33e9a44f4b6b5cf11c1adb4658ce8cbf61a3469ab8a69de5e08c0469b21420d91cff9bbf8d4b0d3819332ef0e3271d5d58b93a79598e119ba1f61b8b893ef57a83c6bbd4bbf2dd11dd7688b7eaefac01707fa0fb1c14976711a468f53c7b7599ac1b6c1b36e96d2f32680e250b2f9d1f7bf7193d4442423be150dbb9b7e88f9773311590cc888b7c93146e9c869aff64029aa405e9a02de9416b1de1fd80ac6fdac97671b88ed413842bdbbe4df0a6d58597d9f1bb0372bfe1ead477ea0920fd1ea8163d17bf85a16a33328d534c93da80b340b1434000e58031dc61005d0fd180e6a14487f716cb16fe16b88a5a18fe297be374f46b99f2a08f971573d23bd50fd55207f764d082c45977064c78ca0c98f5b03aa716306bc0c1b172fcb4d88b6c2f082316863d54798c916edf1e8f676a2d3d13d2400989f093989b19720188136a6402888ca5b6aae2ebe548f2bd13b433efeccc791810119897052bbc6759c9b9c133bafda465b75a2007d875aa59572c04dfad21609c5131bd9f0ed42884c123fac7a35e477de7fcdaf2c6c15e80c7f2de1b7ad5b06fb117a1a56edab8637fd959382fe27bd2cb7b7e0ba2bf58183aac386238fd609472642e995b1dedc27f03ddd11ed8513d43058ebccbad0530dc9fb591ea271bf0b1b19ca21b1722962c2ccb3f36299db873c652e6fce58e5bc7b0ad2e88f7adec42cba039aedfc64da34a85cd47c3af5d27c4edea22a973d5d6c41ee1ce66016ea215a5276ad1b3f1d6e3a016692962a9bf07b77ebfea88e47516d890c8d21d5b21ae20bf3e1422de4c93d5b061bd8478db46472da3e5e09ed7a9fb454eee320b4235392465ee8d32e4e2d719bac58417d831b9c3dce74aec833b48574770779380a3fc55e22da21db976bdf202006299dc9dfe22aa5de25723b1ea5adfb04b8fd2bd57faf1e947f33379978913e7ec27f4e025cf013a7f20a14d910b4f4030a0b0670a5e458d75f5de7b8470964d20e02a18a292d3583a6cb04ca0b88342b61d6030e48bbc07de132df81dc65ad5b3ed95c2adfab886dc91d151a9d4edd1a1d87299830d9b3137644b4cbfbdfcf79ca04845f9f205ef074d52598a0334d44a16d0596c56820ceb0c74c6ed64b4e038380ae235f3a1e03092f703b593e90a111ff06812849a90f4292587df08935efdb3f016c7eed02d8fca01111ebf8cbad23532aff6c0823528961c992e7a60be74c514cbfeb62c3dc915df54e6ea945195ecb1a38ce6a1d6d855aa8e937ad88cc93c2350903fa0ffcb7741d5d69ae61ec19df12b28a847765063a379bf3063f036b000bf4857efac2e94b7e3d4f497d2c30192f31eeef0059b5f1a741b799eb2cad48805df63a89b47c4d7bc3c197848fcf952ce299c4455782b74e8548982cdc5e5419342df6a271bf286c4040ba56b22e7b0999cfe1bc2ab84d82a5a7561de669b30e15153a7526d19fd36705776faeb40f0aabc1ca6017427fa83fc862dc0b6bdcfa4e75acad8a5bfb070e3a38893461fe0bc647b4d478307d5eb5949e57b9895ff33d7ba47210ba78711a0a77669d04cf2a8d0c0af5eb4a1462aff2fb866e200cd1d530bd23c40ca45a8833242363eac30836a34e5a7d62438533cc839950f9f57d822526fa23da72ef4784349ae6d0ca0ef6b88b75895ca69b8163e77c95afd817bfb9f8f215f2d87954f546a007decd0dc668c647572a30343d9d12fa69845d50cd2fc6234942b22b9b6d8b6bd814f72ea1dcaea80d09e992d120228e1f67a8a4b70c6674aacd146f0d881ab8130abe104df4ed61f189f4889b10dc96d83e9bc6e872b54f626379d5e14be79625913b1e579c015c34a47d00a1043436c23bf6b4cdb23582f920d2b9b5546d10cc0a8c2d1590f7e5203d7ad40ca70782c9b35aecfeed035626e5de084fb09f64b20592", + "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c911096761830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000020efbc1b4676f6018902362ead77e27d0000000000000000000000000000000060ec24b679943ff559895776f6f11c442197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e2aadc7250d71617ec5c2f852c2e49ed3180111ef6b130ece8b3504a7e2c3d0372c30f9e94afea47a8631132310e6d72615ccd3227fba92d2ae829172e7bf1c2d24fa1f864f85046e7f1b15accc6c376dfd7c6960cfa3b7fa0662cd10007c0ef7089943510caae41bc6adc5e284e6194b797027236225a98865400f364685fd6609bd127e0b1c620665f422cca7d6dbde8d84465e9268825c7e12feeaf35902160bf36fd6ac0eb33261fdbfd25f0c3cf65632e1be186bed5531a223cf9729cf8d2ea020aec653a1abf28081da5f3283ab32f9ebac85d38f7fb2c6b251fe701d34" + }, + "decryption_aggregator": { + "proof_hex": "0x0000000000000000000000000000000000000000000000043fcf4306e2131cf000000000000000000000000000000000000000000000000c5d67d4dda3528cbf000000000000000000000000000000000000000000000006060d23795c2aa33800000000000000000000000000000000000000000000000000028ba39f65b860000000000000000000000000000000000000000000000003431e382e17ac4ef70000000000000000000000000000000000000000000000021b4003913aea921900000000000000000000000000000000000000000000000c82799839439f89aa00000000000000000000000000000000000000000000000000012d6ba13cd4b700000000000000000000000000000000000000000000000080f5bc15f3c6fe10000000000000000000000000000000000000000000000004914047fa68bd1c0400000000000000000000000000000000000000000000000f6e01eb1aec7be2f20000000000000000000000000000000000000000000000000001f55f29a8b26d00000000000000000000000000000000000000000000000e1f18a47a78f5c09f000000000000000000000000000000000000000000000005849783f26c956b9c00000000000000000000000000000000000000000000000c40e8c3b7b05019240000000000000000000000000000000000000000000000000000b6043b2268b016ecbb30ebefecde0945bf712f33a5e35c605e9140717df8327bb6f5ef69445e0d2588d882879f74634b110eeeabd79da186e72e2bafbf2112924860dbcfdf3c026565a0460bc6cc51c6b1e502aa421fe4416359884492c4229e20ba422abdf41b2b5870384f803e511662e81bacbd584a2d20e25bd8fb8f0fa81166284ee76a1c0dba42cfa440d7ffb3747a6be1cdf331190bb4b1647db3ec65dd98881b1a2f2fe7de0b5b7b7e6fffc7e1dcd6d0a7f3fb92cd074ec90146d9824a3d2c3819ec2682e804a383289c2c9f1eba07cc60c1aff1cb5a6b70be9022d5a7a47e9f8e490c624154e85b5d4fd9b49acb44ca6ac73807ddab9dc20aea1648dd55a2a2cbe2112c1693d6efe05f2d2179bfe2b01d2d969c920d5f342d4a685d6be5b87b5efb1ee16d2b93917750a1dc3e00110ec67da2c9c33cc8ed7e6edef97f126e8ecff128ba7efe8208ae73c22be4d44ba04934379e8eb3fb1dc1c03bfa8b55a3486c5b131f1af9b8e575c05ed7f11c7f427c37e544ffd78db60c7f7b5bbd4da54d96fe002392dba277c15ecff2caa1cbbfa1c0972d054420c5d8655056de823a25f82d1772bcde199450e37df722a55e7dac41a8ee29f07fdf5e9977192943104a95800976f03432f34819ad3555a2bf5d72e9bc63f9d3dfaa9288bdffa426fa258f0e1e671b5758cebbb860ae076f5d73b5d244c234c00e8cb236ea18e6dfb7a6385a2ceb66fb20170a1f3e1904f20a2378844037b1dcadf863f5ef19937364dcc3540e2347c932b8fefb1ec54c8d8e9340e464e115efc55df0e7f56f24e44a8933ba080e285d212dfef11316c6a6f955b9ffca9161b565b183fe3b2819f5eaaada5c06755bf971610b6d70689ea43421cd65c25845da3eabbc145b5f325388497628019fb3cf9bea5175a846b9a4311676de776afdf3a762cc5f00fa4d0c4dfd6804110931e5e2644a05030d33c69fb9f202e3cd2df0341d476aa14e0b4a7d817d572d8b912a899d76b2fd4299063aeb8e49faad0c5f5ea9020cec394e6b0e1e3c9a0006fc8173d358061e3d7ecb78dea762d79411830ef656b635473a7cd16c21121ec372950bbc5726d72ee3f558b3757b55c96b953702a53078156f10783707ef2ac2f9753aa280f1268bcfc51d5de10121343500d12ba3c5f6230a09038cc8631212db3c1be0036f93a42f8a72b1b0f3a175fb9c09c4a31cd3a51d90a1226d6a07e82863c27680a6c050247fbced037c94c32038958b6ddd20115a7c942590c31e77dc1c3f5a6d558f0f0f3181a433a08f7641e3a95703b47e6efd448f0e74fe2d2120c5426c502b3d727e77f80277d8f26607705bd8bc705806a1269768f4c90da9c8d09b0bed9fa2ad2d1913c11030c9c9f9f60dee6cd28733e23e4cdc2717013c759281a65f9c3c13648b4b72430d9b7cb4dd11aa50988a436c113cc112f9042147a805128cf3d0524ab1eca70d990e510686f9d81900d21ea40c4eb8bb071309f1ff7af94fbb0f93e57f882e90bfd5d336d6cb45204d92fc5183aa2da5d92ffd7c5455bdb97f512e60e118641466f139a3926b97a57cec762416b5f039aa266c49d2811485d19e5b8c6d8481def5a7b11b4f6eb0d35a0a934ebdf8e6796f00e4fe8aa6c67a91450e430ec4c6ee2de98eab9780377cb932887af8f7a412aa11c46f63ca887cba374702c014f9181150ea66878ce0bc91126b160e63ad073b1d0ae14032feea6c05413985b0eb692a5b77e2cac56156ff2b5b27245c42913b138e299113f3df18768913b59f15bdefde9c8a535ccb394dc3fca88d77701e260e114f74e35904da232b3161239a94432fc6ed77075589e1d63633ee73427a601923d78b146367f15661597a3bf8bd841f53fdbadceda0f2eb3b9faa4a4351bb304131d0a0cfa4c3956c33343e3a8dec9782b0c12111165539ca255dfbf9a578194039d795df60cb57d147f56442d382c48b64b9cb458db43145a36ef77deedf05dda7c099ccfdbe61ac7687d4c38debfb0f94a862244b520fedc90482aa62521004cb00f3d7dd01f3535b2ebbb943ae0d3d88dba55e4ffcde1b929c4e7bf3f1061d9e25aa39cdfdbd3bb070a330577b342eb8115c2d615e373169af62a93283289bfdd959324811b0f3e3ddd44eb0b9c5cdd7eaae77acc35cbf66799bf6692402f5ef94af7bc25aedbc591d063a5e469a239ce962ad0f484a4e76fd2d4f6d152b35d0eb17c6421293d4c63741ab030a1001d888336e86508ab8b14883d12b771668e1bef33f067dcc1b3eff8730a7ae50bba456a0e0f88d3eb6d9cda64596a02fbc33b4961f1485700545c72d42e5d5f39ae5aa7ceaa64f38730de63b00e5491c72a5d315fa7e6b2f997fb8c32d691022ec660e40c3f79b2e4bb511be4f3e840d4a09bcdb6ef1f63ad37da6601137019a92853119af6d763fe02d9d5da4631b0b2fca10cdac6da89c06471694373ca09119b663aa8f2da003491e3592dd081f2c819ab7e65d912dafe80fbfe0b9d827b3a7d97730ca3997ac5dc303080a9f101be695a1ce3b315c5b47ffc36d829c402e443a78161c63bf19ebf7ebaa362fc71709980d1ed1134fef2ff9ce94dacb7164611513e3efeae83e976603751e74fd1cd0954090ba4339542c911dca7c1e551c1b621fa6e43c3f76af3194bde15ce001879243c4b5d2302612ea4faa1eced4812d0f8a6316825ce217dcbf7f12d932071cdb0dd595478abac873da51a48363ba1bbab6a7c9d33811c71548fd889f5d074019a5fd602e278f49d3a5d78c3eebfd9b15daf24b30eafbb9409820548b5c28d994930e37cfc0ee4f823768bc178f2c3c47c8b0c7f68e18197f4d1c0d2c14296bb2e2d8212c4e41962e429f66ae14fc0b1f5d953fb1a5f5178ad9c3457d20068064f3d23ce29df576e721b4aa8128f2a3a2e0989237b8b4dda0f89087c3252976c194886fdbff8fe106b82fe8cfe21ed4601b6e45d5e7df728f5df112599417f762f1b74d77d040a8ad0e44b935c58f2dfe77aa11f4d20ae469c70b1ea777211f3445db92471b04c84945c86a302d96cbee2ec5da3a441967d52df4ddf5d720fd042f565f49741b508380c4244af3ee17913bae8c9ab320459fb9e5ff6fdd0ac44f8ba61e9e6142cc8e5b29266617f01f913c653d122a01b954c49d601cdc2c5a06c02c720bcc7cf83013663183d11f1e87a2bae50b6521ded294e54bc8f029b1a9f11b22519e4cd52aa674780357b510cbad34face5080d1f612288c27c4291773537798a4fbd7dede6922cae8054535d163609a0d5daa8290c847b682a628798200861f4a7f155b7181b287cb8364a1c034a67e5af9031435f02ae78d0d2064184f366dfedc738ff7893eee8e5dcf0127805d813abcdc7b4a7377e53dcd006e0ac8ea735f93588ea0a71676f3c383859c9beb401c369dd53893251285121cfa07e3616b72cf7fb2edede8a60e8bae1b4d9406e467a75ed676f5c73081a4108b9f31a1dbb2d80e1b4692df14a9ce0f854190ec40a3769c1c738b3a430f5503b15ad4808444dab185ba8e4f56ded101f67a45dd451eadd2395c8c45e2726827ddef201bb70c9c3aa8a54c25d99e908c0ea19b5aa121567061312a5f07f059137b6f541daea8980948fb005bfa6d7f6e369095274292b312f91e641a8b9ea4225d759d940cb55f0a933e46a54d811891c1bf7affd9d037f0667dc58fefd5650fa96cda5692e496cce3fbfcaacf903dfdd71009e62766b6c87f3c5b7222108f0cf144bb82a02a2496ce587a7a23c89452f308e6792254e4d6d8c05b3b2ad825287a68259e53a09024f4f42b7e4c0154e6b67c580cbdb69aed8e412658585d9c16ae813774b518a16cb72e9273072f16fc1e696800108ab5ad68fefb495e92081e9d93ab4c44276fa03bd0ddc6e1cea3826569ca4be8df330d800d3159e9c7b004b02887ac694fdd3c3ca859ed6a55a0dc816f518cd898c788b530c2533c2933151b42e7b40ee1484f3d8bb497312cf4b8aa083dc199675cafde4aa229bb847506888be331d090e12335709ed8fa88d2a3abb1bcad1b9bc912b14163e117f2d6090e580d58e9762e20dc5745277328cbc4fc3e68b13cd5931d13973bcef490492da90e2511ba4f72bf9f5720e54499ce3c0a9bad2a88789682dfbd11fae3dc7f141c57158ac4354f912bd8f94543be5ed9144595eecf5d5b305d5a905aff23301585291ceaaefc89118816b3e053546b4974415cfe209a9ccbcf6b6d27f802e8193e0b015d0b91d9bbc7c16d3f673ec9ceb25f488495cbe9f65e7f64991628d81e4ab818a70c6a1f5c0344f972e3d2580118db18687ac9c61febcd613083e2fb10c96092cc963667d90fc249bb5f0246f77efaa9a624b54810f414a01f285ae91f5ad3517c22645f3a908264c6dff27a5fc3e83513e4dde1b2b4d06e7c0c21212a97c5faac94c396c3eb75609b19a9fdb04d70a404545b7688f3fd1664091e9e01d30bff59042e8eaa895df34bd4c8542de49f23b3b05247e2a60ea7c3746c5c1c82e8522d6fbac54bf45a7b88d466e117d6bbdb3bdb04404be881407c8b347128e4ac64753224c8518132ba80d90e9e36d0660a6420d8ae34883a2d3d3844f71aa13a859439f3ddf740e056cd16330f61f2f5f73491bae01256cc374555869e25aaaed0dd62f477ce2b9fa5dc14b4de837f1fc50e0012ff151c99326b80dc2f08ad72af198ecefe4a5bfc090511a7bd527573e9a71868ae2de0205e2fbec5972f2a267989ab9cefad432cd219000dc7f8ded53bb25544c5eac1c808f9ef87e91e356e43f116f8abf3db5af9c41eed862fb8bbb46ee67a9fe1d14c2b375a4d9c282a9272a09ce77f3a19320181606fa006e4809ead7028014bb9c0b6a272e0b02084794cb26bdb23c1c92e23962a8f80b9347562c76fb9782b08be8f480d4aea0d27502da998f8a4d9521fa0bd2ce332ff8d407451ee3189e4066870ee19128315f0c4621e1fbcbd85523b1b090cace6192c14beb904e071b3ee2c3a51322d9504dc793bce7dc0321b82cb0dec5c082cf850d14e22ef3af036646db4b3d03bb729ed41cb39eb0aceab2405ba0f571af49ff5b88af530fc40aa13614a82d4de4b0b1f3dda6e318d86ffc5181f1ae26c6720d6dfeecfc1d7d428bc80b38889edec04c86df292c6c2397538f796dd154e27f83c090a3918842c43fed2e008760a58004093996bfd42360e58e89a3a837a4f024d9ae4de6e61b9fde360f039ae79142310592732dc009d878c5e24931017ba6e1e127fc916cc487dca718eb0d207461accf461484e7f577dd7a7225398a1a222fbab01fa7b4cb966663decc6f86e911b213dd20e59ecbe262b4f78eed6229f8efcf1181f85bd54056cb9344cef882a0211f77acc433cbc72f660b53633076591101a5debd1c7bb75c477cf57136d912f1172c70849b5c0b2da382b36c1bdb2ec95fea0d963942e49a0bcbdc042fff82a017cc2a52d4e27bb55c769c4c8f25bf40863f646ce50b306e6fccaa3a2bef71bc6ef8cce6579234acca57828c774f693a58271dc056a44bbe3a5711292d86b1bc3f24eacac535dfa3ce20d18337450e14cae37fef5a82affb6bbc9048066902b1445ab1e06c254f4459768a63ee8f16062c8eae5d8eb828f3473f50bd1c4c716dac7dfa5ab8b4ce919e636dcf12a4014526e76d8876e9608df30f9576b5c03127e340a8eacc9bffa81a0a6ab84040c0c51cfbce8d0e898c8468a467c7482a3121bf8817f9896eeb0b803864731ec829f424f7ccec392768c521b4618a6ef3a1f864d74a4a1da3e2f0353e14d3341967d04dbd8b1cc7783f9be1937021b6d010604c7fa35db4d3f9e47cf064b7294832f18521aab04892489887ef26dfbcc3514a06665a91d11641cb70b9ebd9ab5998607a57970b30ed6e1abaa8d9a3d09fc0ba348500a8a70360967e7b2e6506caac7700bb250ae6352a52c80df145e23931ad6bba502bb84c26cc47683d959ab382234d2a6b50288c3a9f57be70087244c00cc036e2ac4050038c045f44e1174ccd51ae2831417eca2ae646ac4dc7aa0531250cc026492f0837dd16254415f01076dede1d0161d12a71091d973065b5b0927cd6e7b6887b563f8e8e38df1a2cf49ce127092ae7f14c16b82eb3bd7c287fe23c93e46dcfff700ddc4db7f0acce19981861fa8741b92a96a89f49d6a2b84641bbba385566e6db0023ca34ee25451f7835e0036587e3cbbfe5c5be17f68df7000bc03f38cbdc90bcf75475ed43684342333c98aa481bdc8933146ae091358ca2458e368f42eec2c19f1adc6c63c133152e072bea6ed1e1a254c040e1f2435ff27a5390a1f10b0afa9098d5b17e8ac84a75e0db6c0bb406d0e3e9c66caf5b32c215216ec3708f27dcb83202ae8c8b1d7832d6eb225a4ff6427d2ecd99fc1017a0e83fbe91a93c8d6ad1bcc9525d7ae40d181022456b13c271a213a7b99f0842317bc405b9565800369c7e9ffde1f1c955b9ee216700826fbee267429b2245e542e5b94f74d0e5d911fd6d861de60a9f9472adbb11091348f7ddd786bdaa3638f200fb4be2cc00aa4bb5754bc487d25643fab3e56ee92601b3d957d9969f0d8751295090f09b4524343335d83d5cb59cbe98b4b30e823fb810141a1db8b1f71032161af45ad8399c25773eef4135083c74ba34ed6e9b3f4ccd8ef4b54bc0db31c136bb4f01423231ec602e6867fb5dd7e2f864bfabd3389e116299ca683d0ba3d167f3caeb2a67f8b70fd97a454cac0decf9f04d6d0821e5304e95aafa64496b81308d84cc84554adff0428ac3e3a774a2bbf5adb3bc4bf670dd3bf87a76b1de22fbc0487db3499fdbfb393424d9c5cb42d832652a6fc484f44bf69307278f9482ab4dccf144cc70309b7d05d855221093137ccf6d29e1b92610d4100b5342a5e2400c944585d2dfff0536223c96d29a24d6b3e9c50c9110a0861c6a1ea7b03801af1f2fb4c61e033a13d3824df9251ea47f41739aa82911366142ecfb7cb757318759290a0356f8624ce873e0b73d1f241762cc941e31fb425c16da148efada514a1e93ec356d035d430e837d2bd1fed9ecfaf76e5cd05eef102799439bfd5fc20a5c7540491780488fdd25770f91535cdbe45cffd1a643d3393c2ff20ae2adb25398281111b51e2b65776c2d724c756ec35997aacdfcf38beb2283c693ab38709cbf556bad78501b91e4f1cc6091157d69450fc37a2063edf6e6a6bd6276915052ab24e1a353629dd9f290ea28da3f7c6267ea34a1e898bd279efead05314f611569998a8ffe0617162dedc7f4a8b77685228bd3665de049cd79924d0874a650138ed3fc5d669eab35ef5bdfe9bda7eaeff28c5432cc4520ff3d59c70407f6e23e3fc2315b7e8950f0d17e4845ba7a6beb01ec848c0a6fe91f99aa47653eac0088cd87378ceb2debee1216f0a3b8dcd699f2adab6bb3bdea59deb6af8b5c30c2bcb893d0ef10dab2ebc892846b6db33ddebb227231a17c6048fa74ee7a741b41b5a262d7db93dc96fe513e5dd0c99b6dd4642e88038d159980292b27b32186313dc8597e5cb810335e3af795e63d11907cab90e7d4c6ada30d42b000c6d438c296ac4d65d5e33bea8bbe7b4a5cd346524c4ba600f30a6847116b9488e4adf341a8d23847c4c67286d0c98fc0ddd2a38379a8b6876c063025972a9b15ffb456c11505c57c8339867f94cbead3d5abe1b40023239a3059b77874c2560f33907142fb27c921b1d03af6980a740462cfad06e7ffe76f8e5e12e59bb75ebeab5b420118fdd54b52424bbdcc621331f7c245bbc0d7b4eaa9d72e58fd40b21e71f558627f775a58502a5871380974b05b73b0ad148ecf99de8dda5c06ca5bd6e0604dc038ba19f12c8c3d2125db1afff20e968de85f6ab442adfc88efbfc9db2ab2c5b0f8fe1650a994f2ec40ae3bdc85a964f349c0529ad0f079f50ea0825ee21805a0c67bfbdd0693878a6f8cfce2ccd0b207f299e10855a7f85a30858d3f2b1d08f0f3084f6a16cae9dfeb648a27dadea1060b707ba1e5c391c47074d2eb57614d50dd5170d37488ff2196a83a2fbaef1b86bc62edbd5290f43132c00b2f3feee1c06717981ddcf1556a368ea88e0ed7e0917d701a94ff2094b41fd5095ee272d8e0dae2ee7650a04d96be40757148a898f64208cbfc043be8f4e78ed5412b9a3d91a777d7ffc8fa796977d83f8747557f7c2ecb1b55fc3ea284aa27e0266801ac81059c18e16b5403cd6a72e8ebed73f27cceba4f1acceae1bef11fdb318866a782eb7ac19600a29eb2526aaf66b2f2c19aebfb169b204ddea661bdb2496dd823f08fe40077c9fc0789e6b255fdf6f504c66dd5788a4cb0fb4b1cae352df34cf3916d124a7f654e6030d923dee25df8bb6ce458e8bba4013fa4a0753c0b468b7c72452a860b8530d332783d11a8dd68841893531fde317fffd7c48cf687d5a05281afb3feba15b0f7a4a93698c66bd046e45c1d45caf4bb242d757935cad8488650dc17c4121cd0a9a26482d62c2d8fa932d55d35bf2740b0f1fdbe7ac2cbd1a7603a8331f04d4514446be3dc504cc93bd9077b19049c7eb8caed8bc1b0b0d72db095a628fda749529e7e816ace5b180d4e1c4976f694e0753b94a26359535cbe525e672ba84b2828e9591ff60308d8269248299d7ce9a3167e391934ff6f9a9c20cb02b80acfd6c2fb601b493bdcf85c28279b3b4ff93701ca5e7c5769b08e6d7087a59ea9816c5d741e423901a061006ba0bcbb3d3e52efa26ae49010eac9aaf13d7c55acff3396b5e084135ed1a839d77aa5b2721adf8f82dd98fbaf497fe7c0cbac63bfa36f446fc0eac7e38904f6341e1492c908ea61b9aa45fc34c535f410a4aa69e11e6bf8db6f7c76a33ed651dbb031dd428abc9caaf72d3b11a5c4d90037aacf2ef9c52e3bb4c090181992129221a6d4f51aa2372e69135d49007397508ea07a57ad9f8d6a6cde315d1a3508867d8b981785965a935741b93fc077452283c34cb8f42bef83f16b50da40b9f7ad1284b86097f4282f311c8e411a1cefe1fe6884b6ad897c5a94e675537a7c72370316c5e81dc79ea6c02ab3f7f5db8d717e1b65dbf02a7e9952306d04bbe149ec0693d4056c54e314a3b1df47bf5e0522648282f42a97a0795bf818a7a5e6b23d809d2b50a22a938d1e6ca54a34f88ee030dec4280569f0c9de1b732b2abe849adc8d3d17c11c614b265f73dc91be83505d75421d8c36a800f9f36974aa23421ca49d4aef71609abc285a1a388fb32550df6ce80be66da801a4e7480d46ea0e5e9d99502f36262d9719d80a9e170d9041ae34238c1c869f7610cd75854b2d3ea32cb7b66688a40f32cfa8d3d810ab1a31e2a643bbb8a85309ee6719083301e02526877c00405e226642024902e68375d1756d87687fbbc344cbbf06fa33e6478df5777c0b315a6a12e78336ff36d99b4221d27f8f2fbe3120e79e072e4ccaf0bd39b96e0ad8adfbe35c8d8097e305a96278800c4551a5c5bb02ab81061b26bc39503fc116a4908b1c1bf3f4a376d504108d514ea4d9e7d05026332025cffba8052964a7f5483be52dd292d5ce87dba2810805b9afb131a08e22e92592637e9544cde42f374c6d19b68e40f55dfa0a3ae27a7731fb7ea0301e0afb15b1767576a8719a646806b834b16b9144d0f076e84232b782ed85f1606d4888008f51e9a11dd1a059e3d32195ce48e2f89484e51c713fd55ebe479825bbabbe4afc094fa4fa57cf797a7f2e68cfc1bbce7d6e16a0210a344b5d40ad3a696a7970363d758038e00d1daaf59ac26cebc9aa26cd5b70a08647d2b39f58ff5f90f97b7dd29a040695ef041b56e7b0ea2e11cd58fcdf9f913e24dd5bc513a9cbd5bdcf2fd9f2390dc816af21190b2ae286406c947c5e9230d1d620b0d6da9d45d5321959f1f1a68d3132d15a6d63c9db114544d286f36730dd550811f27bb81fa9d960323679af5998ff2ca35bb5bab129256929401b5ce06cf4a08a1283dbf1a17ffb32f605c7f867d59c984c19e8ee71745381ec3b1121c9ac918ddf281beca7e65b0a02edc0e093495dc39438b90b578952375c5c8282405c3591ae8b05087da8b63d02892949381a53538020303dccad2c8f354f7ee06f6be0bbc8a9e9ac94b62d7f17cca095ecdfb8d524e589d2526000552540a4517f20bf09584b3188f854be98e36c52df7ad7f2caac301238c6774d439069f6d0c4cffc8ff3314a5edac8b508dfd80c4344d62c58b20097c7f09c884a692aebf207a3f5d4f24dbc711adff884b4476692254bd1c65468ba9af6f09618269385513ef067edadd01488fd08961a820692dfa7c146fc564bee31298e99e70facf472ef5edd30ceccb6509bb6c8b36e0d6aa7caf7ae13d39b03da9de6fb75658c8c60fd102e741097da090dc4fbeebaaf20e09986fba5c2b0688563867ce694584f30aa5549e41a864dc76f9cb46e16965299daeb27097f37c9504b359225090e1cd2119a9239c21218bc5e41527be9e0af64fba84eef0d9ae42eac126a953d6266f03f936235b0a41aa1e90fa49144d65ace190e43c4bfac37f90b4ce7d2a7ae2e80cf3441b45c83689d54c8ea678b55f0371c606c92542a47ca79cabb2a848b4c328a8931ecba586c106bf80eedd0ebbf5513dacea7c721e55d87eb9e9fb2790301c5692614189de796e649f4b1cbe4bdb591ed37bdb60b2888b6dc97b317b90471c50b681838b84b418f86521a3bfa440f93cc750cbd018dee3af17f17dcf8839138e8ce7c1cdf4ea2e6a2d4c73306e3eafc10e1346d1f0d3207a0163319744e82a6cac3587e37bf0b715782e3857d1d33f9b7dae89d768d6099d67b4a8088fb9201d407146252d0c21eafd9064058b57bbef66a660600f7259af3daa9ae54f5917a25cbc22b509da7bcf61d7893916b49102547a36918dd295b364688e24a9f52236962b6d3c4d7f4c720884a66e17711a4c65980b32686e75c2aacf501218b92aa76ea1f17a36d4f4fa62390ae64055246707dd34102b2d669654060b47fd1520b9a97fec0e35191df2192fc484f18d9f1d033b1edd819c76c25387a53e41a2126555fd09f07ebc07017e914cca3ed428574d212d8972223c84850c8d6d4438127d41335492ab521fe12eaccc57d86f489b5986b47d556aa913fa797148c18516391060f38dc28602f6ba7264576da966e1e3bc223586feb02f4d7a46bc6c4426fb3c0a66226ef05c0fc7150e1f93cb7eccf3fc210ba79efdea61948d8d84762f1704114e8d214f766af5050c66c9f4c3650fb0f7f5e703cbb8a20f39cc39481cf7293fb5e42c3ec7f0bd54c722edc383368d99902a3789bb4d2cc813aad2ce045e4621b428b00a31fda45261c00b2d57021a3788742aa01e34669b39aff91507bb0f8c1bcef1a13c62df9de3e3b5321ce04d5d6a649b9146efa63dad75030003d368675c759adbb6257d93437c392fdd59014a8d36363b4465dca69eeb847c242b138e08fca95c703ca16fd5013a086a05b8da3d753ef76f973289a9040c11277d1d29bd91cc0cccd5e3dda88dc9b1da4e3ec1b9d3f19c4f3420d53c704a540d0fca299400df78af938387cd3de12006ee1490c43af52c6d41abf122d801fc1146380d3eb0b489b79b2837c4a32cc7819dab37fe77e61bc3c66d1740a3049413c249513975b8f2c14e2b4ba4dc0e100566cdd44216c3246d213e3b0794142a2fc271d302393b79f5537ab4b6e773c9bebb59d8905236d4ca9d9a4cd2261ace13bdfca0349619b573bf3d9d9472d6120011fbb42805e95f792ef416c40c48d12b7e6c243326b90b87565053d9af997addc85fab2342cbc33a4ab48e9e9d6e6c03ce103989b43389c03fb4372abc67b4b5a7f8e969f06714da30e6e2fd4abc970263d9361ef213f3895ccad00db689edf8953713689ff55a3d88857df96418f80f3b7b782cccfa70134468babdb4365264d3ee33520b528a69370b28b581c4ba1039bd7b1d3ea4092dcd8ba51d1dcf66b0e9c5f7bd65abbf46ec03a2c11ab4b313cc1555bf0198086250dab79aa284be53de9e97d8f77b10076d37fd2586fc0801aa92278061d4d74a4c2327bb0e9fb60054497f2069f3882111851e3bd98e111e8a74585acc65ffad0cc8a2f9636ada525e96aeb41555ec6d95cbc35c7f0b1126febf61577d397dfff012a8a2a2ba0bbc3e346bff4517ffb82bc00218f34a0524275f846dd37a83e31de414c7567d37eae0f205ad5b609b46fd3f15882aa39625ef68e343fe1a24d2afa1181036ada5b05326d9574ed1ac5b80afc7b4cc62b326f5b2dccbbf98d6035c4513c0035ee16e43d1103c8cc9c99854e67097385ab41d08e8e1679f1ac29a79ddf5f048e2fd4af504e440b8426a8701c50d3b75066325635a23c653a00d0cbdcf49e161630d2fc898097c0659ee6bc0b492436e179e126c48725bd68d0a137e650ce703d22705efb8b1b34c5541ad603758ab07b00c155cd691a5170fe43ecfd81acd5d83b73c43d34e7106ea5822943243c8dfb0aa01d8291a2c85f4f3736720a9a4a9fef7245e315b7f056877c2f0784f83c863d90f5a0e575230ef44d9eac80fcd8563f88fc421b32a71fd3bd992ab7040be637d1b7b96a48b6cc3e46a8f412726d81a0f71dda6f61e541015f334561f5ea8f5f50dffef49fc3bb61d6365d4f0656c4acaed1b22ef0f605a3692cf6b2c6409e7262fa5919b268d24cd518c7b0b0bfce0f52cff3430e3acc673b9bceef76dd8ca3712a3c9e0ec13298a1e14bc2c16552c1b2faf1738abb3f14310081c9a295bf210149ce61ab0005ba7b316fe0cc06fd183bffa67c9c2834087c4fce89f944d0052124c65c3174dfd325c017ac48b41beab336f002d852ded401311660139a3b2c811e80b92940a0645dbd238b5986f97c7db3f1d0d0f993227f8f228bf5185075e0d5ab73b10fe7baa0c11c493ee14857248fae31a5584856b343f37a12d52f2182c5146c72d2bf02051af5c1caa8422567fd95f517db9f10f39fc5708e54cc0701cbe6f4556b88bd2551025890388336b1ca7eea6384f5acffaccf30d6efe19c90798b7950ca9d224e0260b9e829c67e369a3516c71cbe7a61d6298d191c90e8d06016db8334d019006c5b96eb38758460acf69fc48db44c032ba9ab6ea87bd59041861986a2f2e00bc684b4d8fda3768075b515de0051cdffb880250b6a221f116f53c7d4176905f62466baf88878854e0362e08f6538c7d8cb15a831f7137db017037ad159d59e1d6c4d17e049240f95ebd7a1375b5a4180a57139fd3b5fd461911925edcb0c0f56ab046085a31edc068b9972e98847326d4e1deb6543879941a4c98a2773587830b03ff781ee11ee55c695af8b876740c8d9cea385c57026e211c03c6f56c393d5685c954b8f15c8c9029a742028af63e6740ef31f528fbf71859b1ae2adbd7e2c1dfbd021fca416afb91d90d39217329217bd19a1f594f980216a12c7b64900752db10e316a778868deda29979ddd21ea57ac38d5a1452e218fd6ad620ffba6ffb5167c3d4f7d0e032fa9a7fb13340190ef68792ed8d909f0902ef3cb391891ee86d04d881f95b67111ab6aec854e716aec6839c7c6ee6e02a64965300938e463bd80f81148e2866d598e6e843e9d4358e602b22660aa6060e9117b91aa7932002ccec550e9e7c58f74c39313db7a2c66bbdc21d53af0e3b246b2fe385873352f19df33b58771673063ca079315826db3d6d2b14c6c7472718a9605d5fea60dc7f671dbd7055fb2e6ee7ca651aadf1482295c13921ce1b3e26818e16427bb2bbd1045da1b8e49e4e3e7d9817026aa5d2f03157626b17a9532da63e4b959b21dd12829c5bde5822056de6e644fc2eacd2d993609bf22b78a50cc023a2f5df4f0ba7448064ce2ec6f5052631c99eab0c66dc208848840733f00e939dd6a730151fe93f7b22655f8c9c443658ba97d6a947809b32ed5510928a2cddc65f807eeaf756acea3fa946c9e05ecc26c68649567d47d66a701f552430214b8173ac6ff586f2754c73f34e024ba6ef907c6e2ec34d83d619980ff4b8e72ce1fb2401f60ba3dbb4d7320768f202bc80968a811ffbe41ee2c0e7e4e9f3071ff4e4f04c8de236dba3638547d57c459c8bdca8616a4a59761480700cadb369218200378fb4db8b25b0d7529f083090a7aa8fb2bf9d1da3e4e66fbf50e262080da0864fbc694139cd3b310bcf206d85b3b97f67cfc0e4455324b7ac1e3c1a711ef87c6b980c5c8cb471c6ac764008f859f06528ca70564c190dd411d45b2bf60a63b2fbf8e5bc7d4f60cf76822c199afeabac46ec638b571b6ffbff2eb203c72de58eb4634937f720fb70c8c37d9f18bdf0e4d18cdd4783aaef321bfd8943211f50104dc0501cfc8e9e2dc443c90290c063028dcf39fae0896f6359bf98c82d00aa9179e566810bf417ad5ee32a551ae96f0870b13dda21cdcbdc7563b320262ee77f55be8f856f5716082c82d13514e14795676ab6f46605b6f3935c3c740903761c60b3dcab05864ec30b350b993d26b3927579587bb91ecc1a9a4090fd832fe502368d2c16d5a190ef0ee84997e5a9878c73aa705283cdebb67a54298da22b886c69c65d60a6dd111d046c1b1c49be26d2e422beca6839024819dbfccf15027203792331b6a713413bd7d6ab58b6533367bae966942128d1b12eb87fb49c1b3ad1a1a34a37ec9e228ec871ece7a964aa11795bd40d1929260abf226aa97a", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed0000000000000000000000000000000020efbc1b4676f6018902362ead77e27d0000000000000000000000000000000060ec24b679943ff559895776f6f11c4429012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022aadc7250d71617ec5c2f852c2e49ed3180111ef6b130ece8b3504a7e2c3d0372c30f9e94afea47a8631132310e6d72615ccd3227fba92d2ae829172e7bf1c2d089943510caae41bc6adc5e284e6194b797027236225a98865400f364685fd6609bd127e0b1c620665f422cca7d6dbde8d84465e9268825c7e12feeaf3590216000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } +} diff --git a/circuits/benchmarks/results_secure_agg/report.md b/circuits/benchmarks/results_secure_agg/report.md new file mode 100644 index 000000000..4cad3ec50 --- /dev/null +++ b/circuits/benchmarks/results_secure_agg/report.md @@ -0,0 +1,214 @@ +# Enclave ZK Circuit Benchmarks + +**Generated:** 2026-05-22 17:01:00 UTC + +**Git Branch:** `feat/1549` +**Git Commit:** `201c73cc0eb6b0030e7c6076b86038baac37fea3` + +**Committee Size:** `H=3`, `N=3`, `T=1` + +## Run configuration + +Settings for this benchmark run (integration test + Nargo circuit benches on the same host). + +### Integration test (`test_trbfv_actor`) + +| Setting | Value | +| ----------------------------------------------------- | -------------------------------------------- | +| Benchmark mode | `secure` | +| BFV preset (artifacts) | `secure-8192` | +| BFV preset (enum) | `SecureThreshold8192` | +| λ (smudging / error) | 60 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | true | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | + +### Hardware & software (Nargo / Barretenberg host) + +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **CPU** | Apple M4 Pro | +| **CPU cores** | 14 | +| **RAM** | 48.00 GB | +| **OS** | Darwin | +| **Architecture** | arm64 | +| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | +| **Barretenberg** | 3.0.0-nightly.20260102 | + +--- + +## Audit status + +On-chain verify gas: **complete** (CRISP Π_user + Enclave Π_DKG / Π_dec replay). + +--- + +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus +(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare +runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, +commit, and hardware. + +--- + +## Protocol Summary + +### Circuit Benchmarks (isolated Nargo + Barretenberg) + +Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). + +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | +| -------------------- | ----------- | --------- | ----------- | ---------- | +| C0 | 287764 | 1.47 | 24.77 | 15.88 | +| C1 | 2432074 | 9.59 | 27.85 | 15.88 | +| C2a | 3879330 | 11.23 | 25.90 | 15.88 | +| C2b | 5739750 | 19.89 | 25.99 | 15.88 | +| C3a | 3764144 | 11.76 | 26.38 | 15.88 | +| C3b | 3764144 | 11.76 | 26.38 | 15.88 | +| C4a | 2564001 | 9.72 | 26.12 | 15.88 | +| C4b | 2564001 | 9.72 | 26.12 | 15.88 | +| C5 | 4395328 | 18.36 | 26.08 | 15.88 | +| user_data_encryption | 1678200 | 6.39 | 29.08 | 15.88 | +| C6 | 3001847 | 10.65 | 27.81 | 15.88 | +| C7 | 128310 | 0.59 | 27.36 | 15.88 | + +### Artifacts + +| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | +| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | +| Π_DKG | 10.69 KB | 0.47 KB | 3042585 | 176232 | 3218817 | +| Π_user | 15.88 KB | 0.12 KB | 2972941 | 193372 | 3166313 | +| Π_dec | 10.69 KB | 3.47 KB | 3553811 | 187392 | 3741203 | + +### Role / Phase / Activity + +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | --------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 1350.54 s | 127.00 KB | 128.56 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 157.91 s | 10.69 KB | 11.16 KB | +| User | P3 | per user input | isolated_nargo | 11.94 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.65 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 389.34 s | 10.69 KB | 14.16 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 70.08 s | 10.69 KB | 14.16 KB | + +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **126.59 s** — +not comparable to P2 wall_clock row above._ + +## Integration test (`test_trbfv_actor`) + +### End-to-end phase timings (integration test) + +| Phase | Metric | Duration (s) | +| ------------------------------------------------------------------ | ------------ | ------------ | +| Starting trbfv actor test | `wall_clock` | 0.00 | +| Setup completed | `wall_clock` | 3.25 | +| Committee Setup Completed | `wall_clock` | 20.24 | +| Committee Finalization Complete | `wall_clock` | 0.00 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 157.91 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 1350.54 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 1357.85 | +| Application CT Gen | `wall_clock` | 7.89 | +| Running FHE Application | `wall_clock` | 0.07 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 70.08 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 389.34 | +| Entire Test | `wall_clock` | 1778.65 | + +### Multithread job timings (`tracked_job_wall`) + +| Name | Avg (s) | Runs | Total (s) | +| ----------------------------- | ------- | ---- | --------- | +| CalculateDecryptionKey | 0.61 | 3 | 1.84 | +| CalculateDecryptionShare | 2.18 | 3 | 6.55 | +| CalculateThresholdDecryption | 1.94 | 1 | 1.94 | +| GenEsiSss | 0.80 | 3 | 2.41 | +| GenPkShareAndSkSss | 1.47 | 3 | 4.41 | +| NodeDkgFold/c2ab_fold | 7.19 | 3 | 21.58 | +| NodeDkgFold/c3a_fold | 51.03 | 3 | 153.08 | +| NodeDkgFold/c3ab_fold | 6.83 | 3 | 20.49 | +| NodeDkgFold/c3b_fold | 55.39 | 3 | 166.16 | +| NodeDkgFold/c4ab_fold | 8.46 | 3 | 25.37 | +| NodeDkgFold/node_fold | 16.32 | 3 | 48.95 | +| ZkDecryptedSharesAggregation | 19.08 | 1 | 19.08 | +| ZkDecryptionAggregation | 50.56 | 1 | 50.56 | +| ZkDkgAggregation | 21.03 | 1 | 21.03 | +| ZkDkgShareDecryption | 52.18 | 6 | 313.05 | +| ZkNodeDkgFold | 145.22 | 3 | 435.65 | +| ZkPkAggregation | 105.56 | 1 | 105.56 | +| ZkPkBfv | 5.97 | 3 | 17.90 | +| ZkPkGeneration | 379.49 | 3 | 1138.47 | +| ZkShareComputation | 101.48 | 6 | 608.87 | +| ZkShareEncryption | 288.67 | 36 | 10392.20 | +| ZkThresholdShareDecryption | 305.78 | 3 | 917.33 | +| ZkVerifyShareDecryptionProofs | 0.11 | 3 | 0.34 | +| ZkVerifyShareProofs | 0.34 | 5 | 1.70 | + +Sum of tracked job wall time: **14474.52 s** — **not** end-to-end latency (jobs run in parallel up +to `BENCHMARK_MULTITHREAD_JOBS`). + +### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) + +| Step | Avg (s) | Runs | Total (s) | +| --------- | ------- | ---- | --------- | +| c2ab_fold | 7.19 | 3 | 21.58 | +| c3a_fold | 51.03 | 3 | 153.08 | +| c3ab_fold | 6.83 | 3 | 20.49 | +| c3b_fold | 55.39 | 3 | 166.16 | +| c4ab_fold | 8.46 | 3 | 25.37 | +| node_fold | 16.32 | 3 | 48.95 | + +### Aggregation jobs (`tracked_job_wall`) + +| Operation | Avg (s) | Runs | Total (s) | +| ---------------------------- | ------- | ---- | --------- | +| ZkDecryptedSharesAggregation | 19.08 | 1 | 19.08 | +| ZkDecryptionAggregation | 50.56 | 1 | 50.56 | +| ZkDkgAggregation | 21.03 | 1 | 21.03 | +| ZkNodeDkgFold | 145.22 | 3 | 435.65 | +| ZkPkAggregation | 105.56 | 1 | 105.56 | + +Sum of aggregation job tracked time: **631.88 s** (parallel CPU work; not P1/P2 wall clock). + +### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) + +| Artifact | Proof (bytes) | Public inputs (bytes) | +| --------------------- | ------------- | --------------------- | +| dkg_aggregator | 10944 | 480 | +| decryption_aggregator | 10944 | 3552 | + +## Raw circuit benchmark JSON (Nargo) + +Source files for the **Circuit Benchmarks** table. Persist this directory with +`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without +re-running the integration test. + +| File | +| ----------------------------------------------------- | +| `config_default.json` | +| `dkg_e_sm_share_computation_default.json` | +| `dkg_pk_default.json` | +| `dkg_share_decryption_default.json` | +| `dkg_share_encryption_default.json` | +| `dkg_sk_share_computation_default.json` | +| `threshold_decrypted_shares_aggregation_default.json` | +| `threshold_pk_aggregation_default.json` | +| `threshold_pk_generation_default.json` | +| `threshold_share_decryption_default.json` | +| `threshold_user_data_encryption_ct0_default.json` | +| `threshold_user_data_encryption_ct1_default.json` | + +## Notes + +- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is + effectively 0. diff --git a/circuits/benchmarks/results_secure_no_agg/benchmark_run_meta.json b/circuits/benchmarks/results_secure_no_agg/benchmark_run_meta.json new file mode 100644 index 000000000..651b5d772 --- /dev/null +++ b/circuits/benchmarks/results_secure_no_agg/benchmark_run_meta.json @@ -0,0 +1,11 @@ +{ + "benchmark_mode": "secure", + "bfv_preset_subdir": "secure-8192", + "proof_aggregation": false, + "multithread_jobs": 13, + "verbose": true, + "nodes_spawned": 20, + "committee_size_n": 3, + "network_model": "in_process_bus", + "testmode_harness": true +} diff --git a/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json b/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json new file mode 100644 index 000000000..a4403f3c3 --- /dev/null +++ b/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json @@ -0,0 +1,88 @@ +{ + "verify_gas": { + "dkg": null, + "user": 2972965, + "dec": null + }, + "source": "folded_proof_export_plus_crisp_verify_test", + "artifact_sizes_bytes": { + "dkg": { + "proof": 0, + "public_inputs": 0 + }, + "dec": { + "proof": 0, + "public_inputs": 0 + } + }, + "calldata_gas": { + "dkg": { + "proof": 0, + "public_inputs": 0, + "total": 0 + }, + "dec": { + "proof": 0, + "public_inputs": 0, + "total": 0 + } + }, + "integration_summary": { + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "secure", + "bfv_preset_subdir": "secure-8192", + "bfv_preset": "SecureThreshold8192", + "lambda": 60, + "proof_aggregation_enabled": false, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": false, + "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, + "operation_timings": [ + { "name": "CalculateDecryptionKey", "avg_seconds": 0.622842069, "runs": 3, "total_seconds": 1.868526208 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 2.173589861, "runs": 3, "total_seconds": 6.520769583 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 1.947012875, "runs": 1, "total_seconds": 1.947012875 }, + { "name": "GenEsiSss", "avg_seconds": 0.750157333, "runs": 3, "total_seconds": 2.250472 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 1.550813236, "runs": 3, "total_seconds": 4.652439708 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 18.934695375, "runs": 1, "total_seconds": 18.934695375 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 55.885590437, "runs": 6, "total_seconds": 335.313542626 }, + { "name": "ZkPkAggregation", "avg_seconds": 49.000496459, "runs": 1, "total_seconds": 49.000496459 }, + { "name": "ZkPkBfv", "avg_seconds": 6.074181486, "runs": 3, "total_seconds": 18.222544459 }, + { "name": "ZkPkGeneration", "avg_seconds": 380.694253347, "runs": 3, "total_seconds": 1142.082760041 }, + { "name": "ZkShareComputation", "avg_seconds": 118.562180048, "runs": 6, "total_seconds": 711.373080291 }, + { "name": "ZkShareEncryption", "avg_seconds": 297.629677555, "runs": 36, "total_seconds": 10714.668392001 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 299.468916014, "runs": 3, "total_seconds": 898.406748043 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.096928694, "runs": 3, "total_seconds": 0.290786082 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.295297283, "runs": 5, "total_seconds": 1.476486417 } + ], + "operation_timings_total_seconds": 13907.008752168, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, + { "label": "Setup completed", "seconds": 3.289690084, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.245420292, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.004953375, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 49.158844, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 1226.639023041, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 1233.9898995, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 7.686523, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.075931042, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 19.353939, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 331.860517917, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 1597.158072, "metric": "wall_clock" } + ], + "folded_artifacts": null + }, + "test_exit_code": { + "crisp": 0, + "folded_export": 0, + "enclave_contracts": 0 + } +} diff --git a/circuits/benchmarks/results_secure_no_agg/integration_summary.json b/circuits/benchmarks/results_secure_no_agg/integration_summary.json new file mode 100644 index 000000000..f6fc72466 --- /dev/null +++ b/circuits/benchmarks/results_secure_no_agg/integration_summary.json @@ -0,0 +1,180 @@ +{ + "integration_test": "test_trbfv_actor", + "benchmark_config": { + "mode": "secure", + "bfv_preset_subdir": "secure-8192", + "bfv_preset": "SecureThreshold8192", + "lambda": 60, + "proof_aggregation_enabled": false, + "multithread_concurrent_jobs": 13, + "committee_h": 3, + "committee_n": 3, + "committee_t": 1, + "nodes_spawned": 20, + "network_model": "in_process_bus", + "testmode_harness": true + }, + "proof_aggregation_enabled": false, + "multithread": { + "rayon_threads": 13, + "max_simultaneous_rayon_tasks": 13, + "cores_available": 14 + }, + "operation_timings": [ + { + "name": "CalculateDecryptionKey", + "avg_seconds": 0.622842069, + "runs": 3, + "total_seconds": 1.868526208 + }, + { + "name": "CalculateDecryptionShare", + "avg_seconds": 2.173589861, + "runs": 3, + "total_seconds": 6.520769583 + }, + { + "name": "CalculateThresholdDecryption", + "avg_seconds": 1.947012875, + "runs": 1, + "total_seconds": 1.947012875 + }, + { + "name": "GenEsiSss", + "avg_seconds": 0.750157333, + "runs": 3, + "total_seconds": 2.250472 + }, + { + "name": "GenPkShareAndSkSss", + "avg_seconds": 1.550813236, + "runs": 3, + "total_seconds": 4.652439708 + }, + { + "name": "ZkDecryptedSharesAggregation", + "avg_seconds": 18.934695375, + "runs": 1, + "total_seconds": 18.934695375 + }, + { + "name": "ZkDkgShareDecryption", + "avg_seconds": 55.885590437, + "runs": 6, + "total_seconds": 335.313542626 + }, + { + "name": "ZkPkAggregation", + "avg_seconds": 49.000496459, + "runs": 1, + "total_seconds": 49.000496459 + }, + { + "name": "ZkPkBfv", + "avg_seconds": 6.074181486, + "runs": 3, + "total_seconds": 18.222544459 + }, + { + "name": "ZkPkGeneration", + "avg_seconds": 380.694253347, + "runs": 3, + "total_seconds": 1142.082760041 + }, + { + "name": "ZkShareComputation", + "avg_seconds": 118.562180048, + "runs": 6, + "total_seconds": 711.373080291 + }, + { + "name": "ZkShareEncryption", + "avg_seconds": 297.629677555, + "runs": 36, + "total_seconds": 10714.668392001 + }, + { + "name": "ZkThresholdShareDecryption", + "avg_seconds": 299.468916014, + "runs": 3, + "total_seconds": 898.406748043 + }, + { + "name": "ZkVerifyShareDecryptionProofs", + "avg_seconds": 0.096928694, + "runs": 3, + "total_seconds": 0.290786082 + }, + { + "name": "ZkVerifyShareProofs", + "avg_seconds": 0.295297283, + "runs": 5, + "total_seconds": 1.476486417 + } + ], + "operation_timings_total_seconds": 13907.008752168, + "operation_timings_metric": "tracked_job_wall", + "phase_timings": [ + { + "label": "Starting trbfv actor test", + "seconds": 0e-9, + "metric": "wall_clock" + }, + { + "label": "Setup completed", + "seconds": 3.289690084, + "metric": "wall_clock" + }, + { + "label": "Committee Setup Completed", + "seconds": 20.245420292, + "metric": "wall_clock" + }, + { + "label": "Committee Finalization Complete", + "seconds": 0.004953375, + "metric": "wall_clock" + }, + { + "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", + "seconds": 49.158844, + "metric": "wall_clock" + }, + { + "label": "ThresholdShares -> PublicKeyAggregated", + "seconds": 1226.639023041, + "metric": "wall_clock" + }, + { + "label": "E3Request -> PublicKeyAggregated", + "seconds": 1233.9898995, + "metric": "wall_clock" + }, + { + "label": "Application CT Gen", + "seconds": 7.686523, + "metric": "wall_clock" + }, + { + "label": "Running FHE Application", + "seconds": 0.075931042, + "metric": "wall_clock" + }, + { + "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", + "seconds": 19.353939, + "metric": "wall_clock" + }, + { + "label": "Ciphertext published -> PlaintextAggregated", + "seconds": 331.860517917, + "metric": "wall_clock" + }, + { + "label": "Entire Test", + "seconds": 1597.158072, + "metric": "wall_clock" + } + ], + "folded_artifacts": null +} diff --git a/circuits/benchmarks/results_secure_no_agg/report.md b/circuits/benchmarks/results_secure_no_agg/report.md new file mode 100644 index 000000000..ffaa7b60b --- /dev/null +++ b/circuits/benchmarks/results_secure_no_agg/report.md @@ -0,0 +1,188 @@ +# Enclave ZK Circuit Benchmarks + +**Generated:** 2026-05-22 16:16:15 UTC + +**Git Branch:** `feat/1549` +**Git Commit:** `201c73cc0eb6b0030e7c6076b86038baac37fea3` + +**Committee Size:** `H=3`, `N=3`, `T=1` + +## Run configuration + +Settings for this benchmark run (integration test + Nargo circuit benches on the same host). + +### Integration test (`test_trbfv_actor`) + +| Setting | Value | +| ----------------------------------------------------- | --------------------- | +| Benchmark mode | `secure` | +| BFV preset (artifacts) | `secure-8192` | +| BFV preset (enum) | `SecureThreshold8192` | +| λ (smudging / error) | 60 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | unknown | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | + +### Hardware & software (Nargo / Barretenberg host) + +| | | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **CPU** | Apple M4 Pro | +| **CPU cores** | 14 | +| **RAM** | 48.00 GB | +| **OS** | Darwin | +| **Architecture** | arm64 | +| **Nargo** | nargo version = 1.0.0-beta.16 noirc version = 1.0.0-beta.16+2d46fca7203545cbbfb31a0d0328de6c10a8db95 (git version hash: 2d46fca7203545cbbfb31a0d0328de6c10a8db95, is dirty: false) | +| **Barretenberg** | 3.0.0-nightly.20260102 | + +--- + +## Audit status + +> **Incomplete on-chain verify gas:** 2 of 3 artifact verify-gas values are **N/A**. Re-run +> `./run_benchmarks.sh` and ensure `extract_crisp_verify_gas.sh` completes (CRISP test + +> `test_trbfv_actor` + EVM replay). Calldata gas alone is not sufficient for audit sign-off. + +--- + +## Measurement methodology + +| Metric kind | Source | Meaning | Do **not** use for | +| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| **wall_clock** | `test_trbfv_actor` phase timers / HLC event span | End-to-end wait in the in-process test harness | Production WAN latency; per-node deployment cost | +| **isolated_nargo** | `benchmark_circuit.sh` per circuit | Single `bb prove` on oracle witness, one circuit at a time | Full protocol pipeline (different witness path) | +| **tracked_job_wall** | `MultithreadReport` per `ComputeRequest` | Wall time of each job on the shared Rayon pool (≤ `BENCHMARK_MULTITHREAD_JOBS` concurrent) | End-to-end time — **sums exceed wall clock** when jobs overlap | + +**Harness limits (integration):** all ciphernodes share one process and bus +(`network_model: in_process_bus`); sortition registers extra nodes; `testmode_*` enabled. Compare +runs only with the same `benchmark_mode`, proof-aggregation flag, `BENCHMARK_MULTITHREAD_JOBS`, +commit, and hardware. + +--- + +## Protocol Summary + +### Circuit Benchmarks (isolated Nargo + Barretenberg) + +Single-circuit `bb prove` on the benchmark oracle witness (not the integration actor pipeline). + +| Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | +| -------------------- | ----------- | --------- | ----------- | ---------- | +| C0 | 287764 | 1.51 | 28.59 | 15.88 | +| C1 | 2432074 | 10.13 | 43.32 | 15.88 | +| C2a | 3879330 | 11.31 | 27.05 | 15.88 | +| C2b | 5739750 | 20.07 | 29.21 | 15.88 | +| C3a | 3764144 | 12.08 | 27.24 | 15.88 | +| C3b | 3764144 | 12.08 | 27.24 | 15.88 | +| C4a | 2564001 | 9.76 | 28.00 | 15.88 | +| C4b | 2564001 | 9.76 | 28.00 | 15.88 | +| C5 | 4395328 | 18.87 | 27.59 | 15.88 | +| user_data_encryption | 1678200 | 6.18 | 28.38 | 15.88 | +| C6 | 3001847 | 10.78 | 27.88 | 15.88 | +| C7 | 128310 | 0.55 | 27.37 | 15.88 | + +### Artifacts + +| Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | +| -------- | ---------- | ----------------- | ---------- | ------------ | --------- | +| Π_DKG | 15.88 KB | 0.12 KB | N/A | 202300 | N/A | +| Π_user | 15.88 KB | 0.12 KB | 2972965 | 193324 | 3166289 | +| Π_dec | 15.88 KB | 3.25 KB | N/A | 188244 | N/A | + +### Role / Phase / Activity + +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | --------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 1226.64 s | 127.00 KB | 128.56 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 49.16 s | 15.88 KB | 16.00 KB | +| User | P3 | per user input | isolated_nargo | 12.00 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.78 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 331.86 s | 15.88 KB | 19.12 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 19.35 s | 15.88 KB | 19.12 KB | + +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **49.00 s** — not +comparable to P2 wall_clock row above._ + +## Integration test (`test_trbfv_actor`) + +### End-to-end phase timings (integration test) + +| Phase | Metric | Duration (s) | +| ------------------------------------------------------------------ | ------------ | ------------ | +| Starting trbfv actor test | `wall_clock` | 0.00 | +| Setup completed | `wall_clock` | 3.29 | +| Committee Setup Completed | `wall_clock` | 20.25 | +| Committee Finalization Complete | `wall_clock` | 0.00 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 49.16 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 1226.64 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 1233.99 | +| Application CT Gen | `wall_clock` | 7.69 | +| Running FHE Application | `wall_clock` | 0.08 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 19.35 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 331.86 | +| Entire Test | `wall_clock` | 1597.16 | + +### Multithread job timings (`tracked_job_wall`) + +| Name | Avg (s) | Runs | Total (s) | +| ----------------------------- | ------- | ---- | --------- | +| CalculateDecryptionKey | 0.62 | 3 | 1.87 | +| CalculateDecryptionShare | 2.17 | 3 | 6.52 | +| CalculateThresholdDecryption | 1.95 | 1 | 1.95 | +| GenEsiSss | 0.75 | 3 | 2.25 | +| GenPkShareAndSkSss | 1.55 | 3 | 4.65 | +| ZkDecryptedSharesAggregation | 18.93 | 1 | 18.93 | +| ZkDkgShareDecryption | 55.89 | 6 | 335.31 | +| ZkPkAggregation | 49.00 | 1 | 49.00 | +| ZkPkBfv | 6.07 | 3 | 18.22 | +| ZkPkGeneration | 380.69 | 3 | 1142.08 | +| ZkShareComputation | 118.56 | 6 | 711.37 | +| ZkShareEncryption | 297.63 | 36 | 10714.67 | +| ZkThresholdShareDecryption | 299.47 | 3 | 898.41 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.29 | +| ZkVerifyShareProofs | 0.30 | 5 | 1.48 | + +Sum of tracked job wall time: **13907.01 s** — **not** end-to-end latency (jobs run in parallel up +to `BENCHMARK_MULTITHREAD_JOBS`). + +### Aggregation jobs (`tracked_job_wall`) + +| Operation | Avg (s) | Runs | Total (s) | +| ---------------------------- | ------- | ---- | --------- | +| ZkDecryptedSharesAggregation | 18.93 | 1 | 18.93 | +| ZkPkAggregation | 49.00 | 1 | 49.00 | + +Sum of aggregation job tracked time: **67.94 s** (parallel CPU work; not P1/P2 wall clock). + +_No `folded_artifacts` in integration summary (export failed or test exited early)._ + +## Raw circuit benchmark JSON (Nargo) + +Source files for the **Circuit Benchmarks** table. Persist this directory with +`crisp_verify_gas.json` (and optional `integration_summary.json`) to regenerate the report without +re-running the integration test. + +| File | +| ----------------------------------------------------- | +| `config_default.json` | +| `dkg_e_sm_share_computation_default.json` | +| `dkg_pk_default.json` | +| `dkg_share_decryption_default.json` | +| `dkg_share_encryption_default.json` | +| `dkg_sk_share_computation_default.json` | +| `threshold_decrypted_shares_aggregation_default.json` | +| `threshold_pk_aggregation_default.json` | +| `threshold_pk_generation_default.json` | +| `threshold_share_decryption_default.json` | +| `threshold_user_data_encryption_ct0_default.json` | +| `threshold_user_data_encryption_ct1_default.json` | + +## Notes + +- All nodes are executed on the same machine in this benchmark run, so inter-node network latency is + effectively 0. diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index 8f8d5f63f..c27bfef4e 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -8,7 +8,7 @@ pragma solidity >=0.8.21; uint256 constant N = 2097152; uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; -uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; +uint256 constant VK_HASH = 0x14b4fe3a693ffd187b26ec6e5d7114f1b2db31e03c8ed3f4c248eeb21b6b5d00; library HonkVerificationKey { function loadVerificationKey() internal @@ -21,50 +21,50 @@ library HonkVerificationKey { publicInputsSize: uint256(31), ql: Honk.G1Point({ x: uint256( - 0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7 + 0x0958727f8e6b167c3ce01ece1a2577e6070fd5908d23fe59eda6a35c2956b826 ), y: uint256( - 0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a + 0x2bc5420d03d0c2416bcd46793cf3588dfac89124cd6ab4f40a899e6ac531dd27 ) }), qr: Honk.G1Point({ x: uint256( - 0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77 + 0x0b6dd3cf30a9e25feaf544219ded9dc02437e61bb1e306bfff98091b74b510d8 ), y: uint256( - 0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94 + 0x02225b1d8f932da744fa83d00b25eafb55b2cff0a60648f5f41e0fa89212c431 ) }), qo: Honk.G1Point({ x: uint256( - 0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507 + 0x27bd5529d88a687fe0cbd2f1dc65d13b367554441d8301281f1ef9b36a7eb1ce ), y: uint256( - 0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda + 0x12349e015d7323e632955a532e07839452f81fbda0a6e47c101d0888fa5f69bd ) }), q4: Honk.G1Point({ x: uint256( - 0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9 + 0x14cb386a7056a199dbb60bda8be9f6bda6248b825eaab0176f778c24631d334b ), y: uint256( - 0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69 + 0x24d8992a7e273a55a42ac837d3ae4eb9cb45776670202138a452c8b71fdb0f6a ) }), qm: Honk.G1Point({ x: uint256( - 0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7 + 0x1aa3cd6a7d3eb455d1a2130eb50220c4936b5406d2b01ca2741238ca207524f6 ), y: uint256( - 0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6 + 0x1778501afed9c76de9eaf7a4d9bb2bb66eb6e6867e77d76dc49dbe8b5f50b789 ) }), qc: Honk.G1Point({ x: uint256( - 0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb + 0x01ee045046d69b516f31758adb206ab84f4816e7c2290326ceb5f50612dd7619 ), y: uint256( - 0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095 + 0x19474d09206a00fb6f8eed0f836b3f0832338dd7292b5673ebb3b17b0ce618ea ) }), qLookup: Honk.G1Point({ @@ -77,90 +77,90 @@ library HonkVerificationKey { }), qArith: Honk.G1Point({ x: uint256( - 0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a + 0x2fef59c5aa902c6d74f45fced7a3248ae6eb0381dfa7bfd3a691ead4114e3dec ), y: uint256( - 0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61 + 0x2b3db764a959504ed3f9c1475b81cdad8af835103d75df014e3d62c45a1a7aeb ) }), qDeltaRange: Honk.G1Point({ x: uint256( - 0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20 + 0x0caf4a2b51a053c7fd0aade49842b0000d8e7ef09a6155af99cbddb3a01ca3de ), y: uint256( - 0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a + 0x0f57bf82d0434ab12375af295208858e5dca1801de11adee2b42b0a62c3b5f73 ) }), qElliptic: Honk.G1Point({ x: uint256( - 0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d + 0x0099ea3f7f7d572dd9a18323b5958cb176ccfa6899b2a1add1d993501b5067b6 ), y: uint256( - 0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170 + 0x0177fce19b1b04dfbb126b714ddd83dcac2e550d6f141b87cbf91a5c1497ad44 ) }), qMemory: Honk.G1Point({ x: uint256( - 0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b + 0x1caef229c2fe5b1a72a94305a53e248caa15e89b31eafa3bc3606ac603f15b62 ), y: uint256( - 0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53 + 0x2414fe66f1180153b95cb96ed95b83e0a4b6693ac85246d11b035efc626a4259 ) }), qNnf: Honk.G1Point({ x: uint256( - 0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c + 0x0309e05930f062f6ffeff4b245730639d250b8a59317834961cabfd18621c1b6 ), y: uint256( - 0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0 + 0x221fb2636f3710e76b2fae7fcf76b5948395d9eaf4bf6b996650e0aa787b7cd1 ) }), qPoseidon2External: Honk.G1Point({ x: uint256( - 0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7 + 0x2b0f1bbabd6f95ad5890e4516679fb74af54a5a45f9ae28ded1e73725aaa40be ), y: uint256( - 0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce + 0x0c37425f8aa86560feaa78caa37b3e9682ee2a87a0cb7b8dbda9769d6f45ad90 ) }), qPoseidon2Internal: Honk.G1Point({ x: uint256( - 0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351 + 0x2787aa1477492c712da27b35f4115efb891fb30572fe45b9d81ab4ec46f1a27e ), y: uint256( - 0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77 + 0x099006d3e16ae9253d3acb3c5e53ca9c55d8efc79d74d2cd5cccb2423d9576c4 ) }), s1: Honk.G1Point({ x: uint256( - 0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189 + 0x23b78d646343f8056a8482427a343dee641eaab6efb05d458744f5a7a4cfb830 ), y: uint256( - 0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc + 0x2452e4c29baa3e0a51ff9cda1fc7e00f6522f11dfe40f093c37fb526ad7e6c42 ) }), s2: Honk.G1Point({ x: uint256( - 0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393 + 0x1ae7ba411c345ac64c36bba0eb84718176724aa2f3df0205d72f9733c1a18b7e ), y: uint256( - 0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c + 0x0e5e836e64262266e7133041f118679aa649b4b5511882161fdf2ecd3371f925 ) }), s3: Honk.G1Point({ x: uint256( - 0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537 + 0x185156ae67d79b65d13c0676c2786c765dc0de33a89edf9ca9f3efaedc705374 ), y: uint256( - 0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea + 0x0dc9bfb2bfc6468800b18af1a1753eaec1c55bb133e4155b590f16e7f1fdec32 ) }), s4: Honk.G1Point({ x: uint256( - 0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab + 0x1b173e123091e39c31eb2203067d1d8c099c6e90b688c47aa25afc4d97b6565d ), y: uint256( - 0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92 + 0x1ec380d3086d115a0316784be7d05ed9c54f0465d5c0f404dfea3e52c7d41ee7 ) }), t1: Honk.G1Point({ @@ -197,34 +197,34 @@ library HonkVerificationKey { }), id1: Honk.G1Point({ x: uint256( - 0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9 + 0x2bc73538ea9e8eeb7917d58ee1d92c61677cfa9d0344a8f4c97bc29ce4018723 ), y: uint256( - 0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c + 0x2ad1b2af81c91ce2016c5cf5c5cb1dba6824f29029ebab60f8543d2c521bb5d9 ) }), id2: Honk.G1Point({ x: uint256( - 0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266 + 0x2824912b8ffffdac70557c216b9d4472f22d0e571124f0d555083cdbaa0d888b ), y: uint256( - 0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883 + 0x1b2ee76cc4d05b22c6775e175ca5eb28f0b3d735b617f1cc53766b6b223183f1 ) }), id3: Honk.G1Point({ x: uint256( - 0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d + 0x18b23eb8e509784f9c56ab322fffcb531bb6616e2e4f1f77e9f4a2fe5fc46772 ), y: uint256( - 0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5 + 0x0d0fb14c991420805a1d310f9605dc96ea838cf12f2f2e264d59b5f6b8ad6d8d ) }), id4: Honk.G1Point({ x: uint256( - 0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a + 0x28a8005794ce64e011078d96be8f37f6337520c10cb11f977a3c7a6731ef7aec ), y: uint256( - 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 + 0x124a06f7822d418a4a0ceba357738929490918f311d898c441e3198c56e946b8 ) }), lagrangeFirst: Honk.G1Point({ @@ -237,10 +237,10 @@ library HonkVerificationKey { }), lagrangeLast: Honk.G1Point({ x: uint256( - 0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400 + 0x0c42e54693f868900a2637f8a1321658762b1458c97b238ffed9617b88d9415e ), y: uint256( - 0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1 + 0x114f2696f738a18b2f1a4a71a3121ac45feab18803d5e22a3c92c1cbe3ae4093 ) }) }); From f5c2fef8490fc34fe7357743220321af9626c879 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 19:09:45 +0200 Subject: [PATCH 34/54] small nits from coderabbit --- examples/CRISP/server/src/cli/commands.rs | 2 +- .../CRISP/server/src/server/routes/voting.rs | 31 ++----------------- .../contracts/Enclave.sol/Enclave.json | 2 +- .../ISlashingManager.json | 2 +- .../CiphernodeRegistryOwnable.json | 2 +- .../contracts/interfaces/ISlashingManager.sol | 7 +++-- .../scripts/configureLocalSlashingPolicies.ts | 2 ++ packages/enclave-contracts/scripts/utils.ts | 14 ++++----- 8 files changed, 19 insertions(+), 43 deletions(-) diff --git a/examples/CRISP/server/src/cli/commands.rs b/examples/CRISP/server/src/cli/commands.rs index 3d7bad7f2..33148d9b6 100644 --- a/examples/CRISP/server/src/cli/commands.rs +++ b/examples/CRISP/server/src/cli/commands.rs @@ -172,6 +172,7 @@ pub async fn initialize_crisp_round( ) .await?; let e3_program: Address = CONFIG.e3_program_address.parse()?; + ensure_e3_program_deployed(e3_program).await?; info!("Enabling E3 Program with address: {}", e3_program); match contract.is_e3_program_enabled(e3_program).await { @@ -191,7 +192,6 @@ pub async fn initialize_crisp_round( } let token_address_str = resolve_voting_token(token_address)?; - ensure_e3_program_deployed(e3_program).await?; info!( "Starting new CRISP round with token address: {} and balance threshold: {}", diff --git a/examples/CRISP/server/src/server/routes/voting.rs b/examples/CRISP/server/src/server/routes/voting.rs index 0f1f06232..d7a906c77 100644 --- a/examples/CRISP/server/src/server/routes/voting.rs +++ b/examples/CRISP/server/src/server/routes/voting.rs @@ -6,11 +6,9 @@ use crate::server::{ app_data::AppData, - database::SledDB, models::{ VoteRequest, VoteResponse, VoteResponseStatus, VoteStatusRequest, VoteStatusResponse, }, - repo::CrispE3Repository, CONFIG, }; use actix_web::{web, HttpResponse, Responder}; @@ -127,14 +125,6 @@ async fn broadcast_encrypted_vote( "[e3_id={}] Failed to decode encoded_proof: {:?}", vote.round_id, e ); - // Rollback voter insertion before returning error - - if !is_vote_update { - let _ = match repo.remove_voter_address(&vote.address).await { - Ok(_) => (), - Err(e) => error!("Error rolling back the vote: {e}"), - }; - } return HttpResponse::BadRequest().json(VoteResponse { status: VoteResponseStatus::FailedBroadcast, @@ -187,7 +177,7 @@ async fn broadcast_encrypted_vote( is_vote_update: Some(is_vote_update), }) } - Err(e) => handle_vote_error(e, repo, &vote.address, has_voted).await, + Err(e) => handle_vote_error(e, has_voted).await, } } @@ -222,26 +212,11 @@ fn extract_error_message(e: &Error) -> String { /// # Arguments /// /// * `e` - The error that occurred -/// * `repo` - The repository to rollback -/// * `address` - The address for the vote -/// * `was_update` - Whether this was a vote update (don't rollback if true) -async fn handle_vote_error( - e: Error, - mut repo: CrispE3Repository, - address: &str, - was_update: bool, -) -> HttpResponse { +/// * `was_update` - Whether this was a vote update +async fn handle_vote_error(e: Error, was_update: bool) -> HttpResponse { // Log the full error for debugging error!("Error while sending vote transaction: {:?}", e); - // Only rollback the vote if this was a new vote, not an update - if !was_update { - match repo.remove_voter_address(address).await { - Ok(_) => (), - Err(err) => error!("Error rolling back the vote: {err}"), - }; - } - let user_message = extract_error_message(&e); HttpResponse::InternalServerError().json(VoteResponse { diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 100bd25c3..7afee35b1 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -2564,5 +2564,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" + "buildInfoId": "solc-0_8_28-5fae32d9d231e92c95043e312da64444ebf9ff4f" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index ff8483662..bdc35df1d 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -1006,5 +1006,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" + "buildInfoId": "solc-0_8_28-5fae32d9d231e92c95043e312da64444ebf9ff4f" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index c299cc656..b5a771bc9 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -1652,5 +1652,5 @@ }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" + "buildInfoId": "solc-0_8_28-5fae32d9d231e92c95043e312da64444ebf9ff4f" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol b/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol index 4f1b08fc4..96f7ed294 100644 --- a/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol +++ b/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol @@ -27,7 +27,8 @@ interface ISlashingManager { * @param ticketPenalty Amount of ticket collateral to slash (in wei) * @param licensePenalty Amount of license bond to slash (in wei) * @param requiresProof Whether this slash type requires cryptographic proof verification - * @param proofVerifier Address of the ISlashVerifier contract for proof validation + * @param proofVerifier Optional ISlashVerifier for ZK-based slashes; Lane A (`proposeSlash`) + * uses on-chain attestation verification and may leave this as `address(0)`. * @param banNode Whether executing this slash will permanently ban the node * @param appealWindow Time window in seconds for operators to appeal (0 = immediate execution, no appeals) * @param enabled Whether this slash type is currently active and can be proposed @@ -371,8 +372,8 @@ interface ISlashingManager { * - reason must not be bytes32(0) * - policy.enabled must be true * - At least one of ticketPenalty or licensePenalty must be non-zero - * - If requiresProof is true, proofVerifier must be set and appealWindow must be 0 - * - If requiresProof is false, appealWindow must be greater than 0 + * - If requiresProof is true (Lane A), appealWindow may be 0 (immediate execute) or > 0 + * - If requiresProof is false (Lane B), appealWindow must be greater than 0 */ function setSlashPolicy( bytes32 reason, diff --git a/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts b/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts index 87471452f..df221f859 100644 --- a/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts +++ b/packages/enclave-contracts/scripts/configureLocalSlashingPolicies.ts @@ -34,6 +34,8 @@ function localAttestationSlashPolicy( ethers: typeof EthersTypes, failureReason: number, ): ISlashingManager.SlashPolicyStruct { + // Lane A (`proposeSlash`): committee attestation is verified in SlashingManager; + // `proofVerifier` is unused (reserved for future ZK verifier wiring). ZeroAddress is intentional. return { ticketPenalty: ethers.parseUnits("10", 6), licensePenalty: ethers.parseEther("50"), diff --git a/packages/enclave-contracts/scripts/utils.ts b/packages/enclave-contracts/scripts/utils.ts index 3a0e00ef7..beb8d1230 100644 --- a/packages/enclave-contracts/scripts/utils.ts +++ b/packages/enclave-contracts/scripts/utils.ts @@ -57,14 +57,12 @@ function resolveRepoRoot(): string { let dir = path.dirname(fileURLToPath(import.meta.url)); const root = path.parse(dir).root; while (dir !== root) { - if ( - fs.existsSync(path.join(dir, "circuits", "bin")) && - fs.existsSync(path.join(dir, "package.json")) - ) { + const pkgPath = path.join(dir, "package.json"); + if (fs.existsSync(pkgPath)) { try { - const pkg = JSON.parse( - fs.readFileSync(path.join(dir, "package.json"), "utf8"), - ) as { name?: string }; + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")) as { + name?: string; + }; if (pkg.name === "@enclave/main") { return dir; } @@ -75,7 +73,7 @@ function resolveRepoRoot(): string { dir = path.dirname(dir); } throw new Error( - "Could not find enclave repo root (expected circuits/bin and package.json name @enclave/main)", + "Could not find enclave repo root (expected package.json name @enclave/main)", ); } From de326b251c623d2f1dcd7f54dad452eaafd21bf3 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 20:44:19 +0200 Subject: [PATCH 35/54] remediation audit --- .../benchmarks/results_insecure_agg/report.md | 5 +- .../results_insecure_no_agg/report.md | 37 ++-- .../benchmarks/results_secure_agg/report.md | 5 +- .../results_secure_no_agg/report.md | 38 ++-- .../benchmarks/scripts/generate_report.sh | 22 ++- .../src/ciphernode_builder.rs | 47 ++++- .../src/enclave_event/committee_finalized.rs | 2 +- crates/tests/tests/integration.rs | 12 ++ .../src/actors/accusation_manager.rs | 124 +++++++++---- .../src/actors/accusation_manager_ext.rs | 10 +- .../tests/slashing_integration_tests.rs | 165 ++++++++++++++---- .../contracts/Enclave.sol/Enclave.json | 2 +- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 2 +- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../ISlashingManager.json | 2 +- .../CiphernodeRegistryOwnable.json | 2 +- .../EnclaveTicketToken.json | 2 +- .../registry/CiphernodeRegistryOwnable.sol | 7 +- .../contracts/slashing/SlashingManager.sol | 74 +++++--- .../verifiers/DkgFoldAttestationVerifier.sol | 16 ++ .../bfv/honk/DkgAggregatorVerifier.sol | 90 +++++----- .../test/E3Lifecycle/E3Integration.spec.ts | 3 + .../test/Slashing/CommitteeExpulsion.spec.ts | 13 ++ .../test/Slashing/SlashingManager.spec.ts | 10 ++ .../test/fixtures/attestation.ts | 60 ++++--- .../bfv_vk_binding/folded_artifacts.json | 8 +- 27 files changed, 539 insertions(+), 223 deletions(-) diff --git a/circuits/benchmarks/results_insecure_agg/report.md b/circuits/benchmarks/results_insecure_agg/report.md index 1125dc797..c8c767db5 100644 --- a/circuits/benchmarks/results_insecure_agg/report.md +++ b/circuits/benchmarks/results_insecure_agg/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-22 14:30:45 UTC +**Generated:** 2026-05-22 17:39:41 UTC **Git Branch:** `feat/1549` -**Git Commit:** `f4cce353bc4f70cd6e3f82e4091a3fee89b67286` +**Git Commit:** `f5c2fef8490fc34fe7357743220321af9626c879` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -27,6 +27,7 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the | Rayon worker threads | 13 | | CPU cores (host) | 14 | | `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | +| Verbose logging (`run_benchmarks.sh --verbose`) | false | ### Hardware & software (Nargo / Barretenberg host) diff --git a/circuits/benchmarks/results_insecure_no_agg/report.md b/circuits/benchmarks/results_insecure_no_agg/report.md index 75a1fe606..94b51a9bd 100644 --- a/circuits/benchmarks/results_insecure_no_agg/report.md +++ b/circuits/benchmarks/results_insecure_no_agg/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-22 14:49:38 UTC +**Generated:** 2026-05-22 17:39:43 UTC **Git Branch:** `feat/1549` -**Git Commit:** `f4cce353bc4f70cd6e3f82e4091a3fee89b67286` +**Git Commit:** `f5c2fef8490fc34fe7357743220321af9626c879` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -13,19 +13,21 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the ### Integration test (`test_trbfv_actor`) -| Setting | Value | -| ----------------------------------------------------- | ---------------------- | -| Benchmark mode | `insecure` | -| BFV preset (artifacts) | `insecure-512` | -| BFV preset (enum) | `InsecureThreshold512` | -| λ (smudging / error) | 2 | -| Nodes spawned (builder) | 20 | -| Network model | `in_process_bus` | -| Testmode harness | true | -| `proof_aggregation_enabled` | unknown | -| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | -| Rayon worker threads | 13 | -| CPU cores (host) | 14 | +| Setting | Value | +| ----------------------------------------------------- | ------------------------------------ | +| Benchmark mode | `insecure` | +| BFV preset (artifacts) | `insecure-512` | +| BFV preset (enum) | `InsecureThreshold512` | +| λ (smudging / error) | 2 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | false | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| `dkg_fold_attestation_verifier` | _(disabled — proof aggregation off)_ | +| Verbose logging (`run_benchmarks.sh --verbose`) | false | ### Hardware & software (Nargo / Barretenberg host) @@ -149,6 +151,9 @@ comparable to P2 wall_clock row above._ Sum of tracked job wall time: **298.50 s** — **not** end-to-end latency (jobs run in parallel up to `BENCHMARK_MULTITHREAD_JOBS`). +_Baseline run: node DKG folds and folded Π_DKG / Π_dec export are disabled. Compare with +`BENCHMARK_PROOF_AGGREGATION=true` (default)._ + ### Aggregation jobs (`tracked_job_wall`) | Operation | Avg (s) | Runs | Total (s) | @@ -158,8 +163,6 @@ Sum of tracked job wall time: **298.50 s** — **not** end-to-end latency (jobs Sum of aggregation job tracked time: **10.57 s** (parallel CPU work; not P1/P2 wall clock). -_No `folded_artifacts` in integration summary (export failed or test exited early)._ - ## Raw circuit benchmark JSON (Nargo) Source files for the **Circuit Benchmarks** table. Persist this directory with diff --git a/circuits/benchmarks/results_secure_agg/report.md b/circuits/benchmarks/results_secure_agg/report.md index 4cad3ec50..da3a023c0 100644 --- a/circuits/benchmarks/results_secure_agg/report.md +++ b/circuits/benchmarks/results_secure_agg/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-22 17:01:00 UTC +**Generated:** 2026-05-22 17:39:45 UTC **Git Branch:** `feat/1549` -**Git Commit:** `201c73cc0eb6b0030e7c6076b86038baac37fea3` +**Git Commit:** `f5c2fef8490fc34fe7357743220321af9626c879` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -27,6 +27,7 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the | Rayon worker threads | 13 | | CPU cores (host) | 14 | | `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | +| Verbose logging (`run_benchmarks.sh --verbose`) | false | ### Hardware & software (Nargo / Barretenberg host) diff --git a/circuits/benchmarks/results_secure_no_agg/report.md b/circuits/benchmarks/results_secure_no_agg/report.md index ffaa7b60b..77bac7f12 100644 --- a/circuits/benchmarks/results_secure_no_agg/report.md +++ b/circuits/benchmarks/results_secure_no_agg/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-22 16:16:15 UTC +**Generated:** 2026-05-22 17:39:46 UTC **Git Branch:** `feat/1549` -**Git Commit:** `201c73cc0eb6b0030e7c6076b86038baac37fea3` +**Git Commit:** `f5c2fef8490fc34fe7357743220321af9626c879` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -13,20 +13,21 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the ### Integration test (`test_trbfv_actor`) -| Setting | Value | -| ----------------------------------------------------- | --------------------- | -| Benchmark mode | `secure` | -| BFV preset (artifacts) | `secure-8192` | -| BFV preset (enum) | `SecureThreshold8192` | -| λ (smudging / error) | 60 | -| Nodes spawned (builder) | 20 | -| Network model | `in_process_bus` | -| Testmode harness | true | -| `proof_aggregation_enabled` | unknown | -| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | -| Rayon worker threads | 13 | -| CPU cores (host) | 14 | -| Verbose logging (`run_benchmarks.sh --verbose`) | true | +| Setting | Value | +| ----------------------------------------------------- | ------------------------------------ | +| Benchmark mode | `secure` | +| BFV preset (artifacts) | `secure-8192` | +| BFV preset (enum) | `SecureThreshold8192` | +| λ (smudging / error) | 60 | +| Nodes spawned (builder) | 20 | +| Network model | `in_process_bus` | +| Testmode harness | true | +| `proof_aggregation_enabled` | false | +| `BENCHMARK_MULTITHREAD_JOBS` (max concurrent ZK jobs) | 13 | +| Rayon worker threads | 13 | +| CPU cores (host) | 14 | +| `dkg_fold_attestation_verifier` | _(disabled — proof aggregation off)_ | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | ### Hardware & software (Nargo / Barretenberg host) @@ -150,6 +151,9 @@ comparable to P2 wall_clock row above._ Sum of tracked job wall time: **13907.01 s** — **not** end-to-end latency (jobs run in parallel up to `BENCHMARK_MULTITHREAD_JOBS`). +_Baseline run: node DKG folds and folded Π_DKG / Π_dec export are disabled. Compare with +`BENCHMARK_PROOF_AGGREGATION=true` (default)._ + ### Aggregation jobs (`tracked_job_wall`) | Operation | Avg (s) | Runs | Total (s) | @@ -159,8 +163,6 @@ to `BENCHMARK_MULTITHREAD_JOBS`). Sum of aggregation job tracked time: **67.94 s** (parallel CPU work; not P1/P2 wall clock). -_No `folded_artifacts` in integration summary (export failed or test exited early)._ - ## Raw circuit benchmark JSON (Nargo) Source files for the **Circuit Benchmarks** table. Persist this directory with diff --git a/circuits/benchmarks/scripts/generate_report.sh b/circuits/benchmarks/scripts/generate_report.sh index d541fd983..c302f090c 100755 --- a/circuits/benchmarks/scripts/generate_report.sh +++ b/circuits/benchmarks/scripts/generate_report.sh @@ -485,7 +485,17 @@ emit_run_configuration_section() { bench_preset=$(jq -r '.benchmark_config.bfv_preset_subdir // empty' <<<"$blob") bench_preset_name=$(jq -r '.benchmark_config.bfv_preset // empty' <<<"$blob") bench_lambda=$(jq -r '.benchmark_config.lambda // empty' <<<"$blob") - proof_agg=$(jq -r '.benchmark_config.proof_aggregation_enabled // .proof_aggregation_enabled // empty' <<<"$blob") + # NOTE: cannot use `//` here — jq's alt-operator treats `false` as null + # and would fall through, dropping the legitimate `false` value. + proof_agg=$(jq -r ' + if .benchmark_config.proof_aggregation_enabled != null then + .benchmark_config.proof_aggregation_enabled + elif .proof_aggregation_enabled != null then + .proof_aggregation_enabled + else + empty + end + ' <<<"$blob") multithread_jobs=$(jq -r '.benchmark_config.multithread_concurrent_jobs // .multithread.max_simultaneous_rayon_tasks // empty' <<<"$blob") rayon_threads=$(jq -r '.multithread.rayon_threads // empty' <<<"$blob") cores_avail=$(jq -r '.multithread.cores_available // empty' <<<"$blob") @@ -497,9 +507,9 @@ emit_run_configuration_section() { if [ -n "$meta_file" ]; then [ -z "$bench_mode" ] || [ "$bench_mode" = "null" ] && bench_mode=$(jq -r '.benchmark_mode // empty' "$meta_file") [ -z "$bench_preset" ] || [ "$bench_preset" = "null" ] && bench_preset=$(jq -r '.bfv_preset_subdir // empty' "$meta_file") - [ -z "$proof_agg" ] || [ "$proof_agg" = "null" ] && proof_agg=$(jq -r '.proof_aggregation // empty' "$meta_file") + [ -z "$proof_agg" ] || [ "$proof_agg" = "null" ] && proof_agg=$(jq -r 'if .proof_aggregation != null then .proof_aggregation else empty end' "$meta_file") [ -z "$multithread_jobs" ] || [ "$multithread_jobs" = "null" ] && multithread_jobs=$(jq -r '.multithread_jobs // empty' "$meta_file") - verbose_flag=$(jq -r '.verbose // empty' "$meta_file") + verbose_flag=$(jq -r 'if .verbose != null then .verbose else empty end' "$meta_file") fi [ -z "$bench_mode" ] || [ "$bench_mode" = "null" ] && bench_mode="unknown" @@ -538,7 +548,7 @@ emit_run_configuration_section() { local nodes_spawned network_model testmode_harness nodes_spawned=$(jq -r '.benchmark_config.nodes_spawned // empty' <<<"$blob") network_model=$(jq -r '.benchmark_config.network_model // empty' <<<"$blob") - testmode_harness=$(jq -r '.benchmark_config.testmode_harness // empty' <<<"$blob") + testmode_harness=$(jq -r 'if .benchmark_config.testmode_harness != null then .benchmark_config.testmode_harness else empty end' <<<"$blob") if [ -n "$nodes_spawned" ] && [ "$nodes_spawned" != "null" ]; then echo "| Nodes spawned (builder) | $nodes_spawned |" fi @@ -812,7 +822,9 @@ if [ -n "$INTEGRATION_BLOB" ]; then fi fi - agg_enabled=$(jq -r '.proof_aggregation_enabled // true' <<<"$INTEGRATION_BLOB") + # NOTE: cannot use `// true` here — jq's `//` treats `false` as null and + # would incorrectly return `true` when aggregation is explicitly disabled. + agg_enabled=$(jq -r 'if .proof_aggregation_enabled == false then "false" else "true" end' <<<"$INTEGRATION_BLOB") if [ "$agg_enabled" = "false" ]; then echo "" >> "$OUTPUT_FILE" echo "_Baseline run: node DKG folds and folded Π_DKG / Π_dec export are disabled. Compare with \`BENCHMARK_PROOF_AGGREGATION=true\` (default)._" >> "$OUTPUT_FILE" diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 7519aa698..469bcc56c 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -84,6 +84,8 @@ pub struct CiphernodeBuilder { zk_backend: Option, /// Test/benchmark: EIP-712 verifying contract for DKG fold attestations (no RPC required). dkg_fold_attestation_verifier: Option
, + /// Test/benchmark: EIP-712 verifying contract for accusation votes (no RPC required). + slashing_manager: Option
, net_config: Option, ignore_address_check: bool, global_shared_store: bool, @@ -153,6 +155,7 @@ impl CiphernodeBuilder { testmode_signer: None, threshold_plaintext_agg: false, dkg_fold_attestation_verifier: None, + slashing_manager: None, net_config: None, zk_backend: None, ignore_address_check: false, @@ -223,15 +226,48 @@ impl CiphernodeBuilder { self } + /// Benchmark/test: set slashing manager address (EIP-712 verifyingContract for + /// accusation votes) without configuring EVM chains (no RPC). + pub fn testmode_with_slashing_manager(mut self, slashing_manager: Address) -> Self { + self.slashing_manager = Some(slashing_manager); + self + } + + fn resolve_slashing_manager(&self) -> Result
{ + if let Some(addr) = self.slashing_manager { + return Ok(addr); + } + self.chains + .first() + .and_then(|c| c.contracts.slashing_manager.as_ref()) + .map(|c| c.address()) + .transpose()? + .ok_or_else(|| { + anyhow::anyhow!( + "`slashing_manager` contract address is required in chain config — \ + it is the EIP-712 `verifyingContract` for accusation vote signatures" + ) + }) + } + fn resolve_dkg_fold_attestation_verifier(&self) -> Result> { if let Some(addr) = self.dkg_fold_attestation_verifier { return Ok(Some(addr)); } - self.chains + let resolved = self + .chains .first() .and_then(|c| c.contracts.dkg_fold_attestation_verifier.as_ref()) .map(|c| c.address()) - .transpose() + .transpose()?; + if resolved.is_none() { + tracing::warn!( + "`dkg_fold_attestation_verifier` contract address is not set in chain config. \ + If any E3 enables `proof_aggregation_enabled`, this node will fail to sign \ + DKG fold attestations and be counted as dishonest." + ); + } + Ok(resolved) } /// Log data actor events @@ -545,8 +581,13 @@ impl CiphernodeBuilder { // AccusationManager extension — per-E3 fault attribution quorum { let signer = provider_cache.ensure_signer().await?; + let slashing_manager_addr = self.resolve_slashing_manager()?; info!("Setting up AccusationManagerExtension"); - e3_builder = e3_builder.with(AccusationManagerExtension::create(&bus, signer)); + e3_builder = e3_builder.with(AccusationManagerExtension::create( + &bus, + signer, + slashing_manager_addr, + )); } // CommitmentConsistencyChecker extension — per-E3 cross-circuit commitment validation diff --git a/crates/events/src/enclave_event/committee_finalized.rs b/crates/events/src/enclave_event/committee_finalized.rs index a1c2e9b72..01f602952 100644 --- a/crates/events/src/enclave_event/committee_finalized.rs +++ b/crates/events/src/enclave_event/committee_finalized.rs @@ -21,7 +21,7 @@ pub struct CommitteeFinalized { impl CommitteeFinalized { /// Sort committee members by ascending address so every node derives the same /// deterministic ordering, matching the on-chain registry's canonical address-ascending - /// `topNodes` layout (see `_sortTopNodesByAscendingScore` in `CiphernodeRegistryOwnable`). + /// `topNodes` layout (see `_sortTopNodesByAscendingAddress` in `CiphernodeRegistryOwnable`). /// The node with the numerically lowest address ends up at index 0 (= party 0). /// Address comparison is done in lowercase to be independent of EIP-55 checksumming. pub fn sort_by_score(&mut self) { diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 095806b84..b30003cc5 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -152,6 +152,15 @@ fn benchmark_dkg_fold_attestation_verifier_address() -> Option
{ addr.parse().ok() } +/// Slashing manager address for benchmarks (no live RPC; used as EIP-712 +/// `verifyingContract` for accusation vote signatures). +fn benchmark_slashing_manager_address() -> Address { + let addr = std::env::var("BENCHMARK_SLASHING_MANAGER") + .unwrap_or_else(|_| "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707".to_string()); + addr.parse() + .expect("BENCHMARK_SLASHING_MANAGER must be a valid address") +} + /// RAII guard that restores the benchmark-specific collector-timeout env vars on scope exit. /// This prevents leaking secure-mode tuning into other tests/processes. struct EnvTimeoutVarsGuard { @@ -1142,6 +1151,7 @@ async fn test_trbfv_actor() -> Result<()> { // Actor system setup let concurrent_jobs = benchmark_multithread_concurrent_jobs(); let dkg_fold_verifier = benchmark_dkg_fold_attestation_verifier_address(); + let slashing_manager_addr = benchmark_slashing_manager_address(); let max_threadroom = Multithread::get_max_threads_minus(1); let task_pool = Multithread::create_taskpool(max_threadroom, concurrent_jobs); let multithread_report = MultithreadReport::new(max_threadroom, concurrent_jobs).start(); @@ -1170,6 +1180,7 @@ async fn test_trbfv_actor() -> Result<()> { .with_threshold_plaintext_aggregation() .testmode_with_forked_bus(bus.event_bus()) .testmode_ignore_address_check() + .testmode_with_slashing_manager(slashing_manager_addr) .with_logging(); if let Some(verifier) = dkg_fold_verifier { b = b.testmode_with_dkg_fold_attestation_verifier(verifier); @@ -1194,6 +1205,7 @@ async fn test_trbfv_actor() -> Result<()> { .with_threshold_plaintext_aggregation() .testmode_with_forked_bus(bus.event_bus()) .testmode_ignore_address_check() + .testmode_with_slashing_manager(slashing_manager_addr) .with_logging(); if let Some(verifier) = dkg_fold_verifier { b = b.testmode_with_dkg_fold_attestation_verifier(verifier); diff --git a/crates/zk-prover/src/actors/accusation_manager.rs b/crates/zk-prover/src/actors/accusation_manager.rs index eb4b12366..63fcf13f8 100644 --- a/crates/zk-prover/src/actors/accusation_manager.rs +++ b/crates/zk-prover/src/actors/accusation_manager.rs @@ -117,6 +117,9 @@ pub struct AccusationManager { my_address: Address, signer: PrivateKeySigner, + /// On-chain `SlashingManager` address (EIP-712 `verifyingContract` for vote signatures). + slashing_manager: Address, + /// All committee member addresses for this E3. committee: Vec
, /// Quorum threshold — matches the cryptographic threshold M. @@ -152,6 +155,7 @@ impl AccusationManager { bus: &BusHandle, e3_id: E3id, signer: PrivateKeySigner, + slashing_manager: Address, committee: Vec
, threshold_m: usize, params_preset: e3_fhe_params::BfvPreset, @@ -162,6 +166,7 @@ impl AccusationManager { e3_id, my_address, signer, + slashing_manager, committee, threshold_m, pending: HashMap::new(), @@ -178,11 +183,21 @@ impl AccusationManager { bus: &BusHandle, e3_id: E3id, signer: PrivateKeySigner, + slashing_manager: Address, committee: Vec
, threshold_m: usize, params_preset: e3_fhe_params::BfvPreset, ) -> Addr { - let addr = Self::new(bus, e3_id, signer, committee, threshold_m, params_preset).start(); + let addr = Self::new( + bus, + e3_id, + signer, + slashing_manager, + committee, + threshold_m, + params_preset, + ) + .start(); bus.subscribe(EventType::ProofVerificationFailed, addr.clone().into()); bus.subscribe(EventType::ProofVerificationPassed, addr.clone().into()); bus.subscribe(EventType::ProofFailureAccusation, addr.clone().into()); @@ -276,53 +291,78 @@ impl AccusationManager { } } - fn sign_vote_digest(&self, vote: &AccusationVote) -> Vec { - let digest = Self::vote_digest(vote); - self.signer - .sign_message_sync(&digest) - .map(|sig| sig.as_bytes().to_vec()) - .unwrap_or_default() + fn sign_vote_digest(&self, vote: &AccusationVote) -> Result, alloy::signers::Error> { + let digest = Self::vote_digest(vote, self.slashing_manager); + // `sign_hash_sync` signs the raw 32-byte hash without EIP-191 wrapping, + // which is what EIP-712 requires (`digest` is already the + // `\x19\x01 || domainSeparator || structHash` hash). + let sig = self.signer.sign_hash_sync(&digest.into())?; + Ok(sig.as_bytes().to_vec()) } - /// Structured digest for ECDSA signing of votes. - /// - /// ```text - /// keccak256(abi.encode( - /// VOTE_TYPEHASH, - /// chainId, e3Id, accusationId, voter, agrees, - /// dataHash - /// )) - /// ``` - fn vote_digest(vote: &AccusationVote) -> [u8; 32] { + /// Canonical EIP-712 domain separator for vote signatures. + /// Must match `SlashingManager`'s domain construction: + /// `keccak256(abi.encode(EIP712_DOMAIN_TYPEHASH, DOMAIN_NAME_HASH, DOMAIN_VERSION_HASH, + /// chainId, verifyingContract))`. + fn vote_domain_separator(chain_id: u64, verifying_contract: Address) -> [u8; 32] { + let domain_typehash: [u8; 32] = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", + ) + .into(); + let name_hash: [u8; 32] = keccak256("EnclaveSlashingManager").into(); + let version_hash: [u8; 32] = keccak256("1").into(); + let encoded = ( + domain_typehash, + name_hash, + version_hash, + U256::from(chain_id), + verifying_contract, + ) + .abi_encode(); + keccak256(&encoded).into() + } + + /// Canonical EIP-712 typed-data hash for a vote. + /// `keccak256("\x19\x01" || domainSeparator || structHash)` where + /// `structHash = keccak256(abi.encode(VOTE_TYPEHASH, e3Id, accusationId, voter, agrees, dataHash))`. + fn vote_digest(vote: &AccusationVote, verifying_contract: Address) -> [u8; 32] { let e3_id_u256: U256 = vote .e3_id .clone() .try_into() .expect("E3id should be valid U256"); let typehash: [u8; 32] = keccak256( - "AccusationVote(uint256 chainId,uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)" + "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)" ).into(); - let encoded = ( - typehash, - U256::from(vote.e3_id.chain_id()), - e3_id_u256, - vote.accusation_id, - vote.voter, - vote.agrees, - vote.data_hash, + let struct_hash: [u8; 32] = keccak256( + &( + typehash, + e3_id_u256, + vote.accusation_id, + vote.voter, + vote.agrees, + vote.data_hash, + ) + .abi_encode(), ) - .abi_encode(); - keccak256(&encoded).into() + .into(); + let domain = Self::vote_domain_separator(vote.e3_id.chain_id(), verifying_contract); + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(&domain); + buf.extend_from_slice(&struct_hash); + keccak256(&buf).into() } fn verify_vote_signature(&self, vote: &AccusationVote) -> bool { - let digest = Self::vote_digest(vote); + let digest = Self::vote_digest(vote, self.slashing_manager); let sig = match alloy::primitives::Signature::try_from(vote.signature.extract_bytes().as_ref()) { Ok(s) => s, Err(_) => return false, }; - match sig.recover_address_from_msg(&digest) { + match sig.recover_address_from_prehash(&digest.into()) { Ok(addr) => addr == vote.voter, Err(_) => false, } @@ -526,7 +566,13 @@ impl AccusationManager { data_hash, signature: ArcBytes::default(), }; - own_vote.signature = ArcBytes::from_bytes(&self.sign_vote_digest(&own_vote)); + match self.sign_vote_digest(&own_vote) { + Ok(sig) => own_vote.signature = ArcBytes::from_bytes(&sig), + Err(err) => { + error!("Failed to sign own AccusationVote: {err}"); + return; + } + } if let Err(err) = self.bus.publish(own_vote.clone(), ec.clone()) { error!("Failed to broadcast own AccusationVote: {err}"); @@ -739,7 +785,13 @@ impl AccusationManager { data_hash: our_data_hash, signature: ArcBytes::default(), }; - vote.signature = ArcBytes::from_bytes(&self.sign_vote_digest(&vote)); + match self.sign_vote_digest(&vote) { + Ok(sig) => vote.signature = ArcBytes::from_bytes(&sig), + Err(err) => { + error!("Failed to sign AccusationVote: {err}"); + return; + } + } info!( "Voting {} on accusation against {} for {:?}", @@ -1192,7 +1244,13 @@ impl AccusationManager { data_hash: reverif.data_hash, signature: ArcBytes::default(), }; - vote.signature = ArcBytes::from_bytes(&self.sign_vote_digest(&vote)); + match self.sign_vote_digest(&vote) { + Ok(sig) => vote.signature = ArcBytes::from_bytes(&sig), + Err(err) => { + error!("Failed to sign C3a/C3b AccusationVote: {err}"); + return; + } + } info!( "C3a/C3b re-verification complete — voting {} on accusation against {:?}", diff --git a/crates/zk-prover/src/actors/accusation_manager_ext.rs b/crates/zk-prover/src/actors/accusation_manager_ext.rs index 9fce515c8..85909b6b4 100644 --- a/crates/zk-prover/src/actors/accusation_manager_ext.rs +++ b/crates/zk-prover/src/actors/accusation_manager_ext.rs @@ -22,13 +22,20 @@ use tracing::{error, info}; pub struct AccusationManagerExtension { bus: BusHandle, signer: PrivateKeySigner, + /// On-chain `SlashingManager` address (EIP-712 `verifyingContract` for vote sigs). + slashing_manager: Address, } impl AccusationManagerExtension { - pub fn create(bus: &BusHandle, signer: PrivateKeySigner) -> Box { + pub fn create( + bus: &BusHandle, + signer: PrivateKeySigner, + slashing_manager: Address, + ) -> Box { Box::new(Self { bus: bus.clone(), signer: signer.clone(), + slashing_manager, }) } } @@ -87,6 +94,7 @@ impl E3Extension for AccusationManagerExtension { &self.bus, e3_id, self.signer.clone(), + self.slashing_manager, committee_addresses, threshold_m, meta.params_preset, diff --git a/crates/zk-prover/tests/slashing_integration_tests.rs b/crates/zk-prover/tests/slashing_integration_tests.rs index b55b21988..1e2157541 100644 --- a/crates/zk-prover/tests/slashing_integration_tests.rs +++ b/crates/zk-prover/tests/slashing_integration_tests.rs @@ -372,7 +372,12 @@ fn test_digest_matches_solidity_encoding() { // ════════════════════════════════════════════════════════════════════════════ const VOTE_TYPEHASH_STR: &str = - "AccusationVote(uint256 chainId,uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)"; + "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)"; + +const VOTE_DOMAIN_TYPEHASH_STR: &str = + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; +const VOTE_DOMAIN_NAME: &str = "EnclaveSlashingManager"; +const VOTE_DOMAIN_VERSION: &str = "1"; /// Lane A policy key: `keccak256(abi.encodePacked(proofType))` (must match `SlashingManager.proposeSlash`). fn reason_for_proof_type(proof_type: u8) -> FixedBytes<32> { @@ -409,9 +414,28 @@ fn compute_accusation_id( ) } -/// Compute the structured vote digest matching `AccusationManager::vote_digest()`. +/// Compute the canonical EIP-712 vote domain separator. +fn compute_vote_domain_separator(chain_id: u64, verifying_contract: Address) -> FixedBytes<32> { + let domain_typehash = keccak256(VOTE_DOMAIN_TYPEHASH_STR); + let name_hash = keccak256(VOTE_DOMAIN_NAME.as_bytes()); + let version_hash = keccak256(VOTE_DOMAIN_VERSION.as_bytes()); + keccak256( + &( + domain_typehash, + name_hash, + version_hash, + U256::from(chain_id), + verifying_contract, + ) + .abi_encode(), + ) +} + +/// Compute the canonical EIP-712 typed-data hash for a vote, matching +/// `AccusationManager::vote_digest()` and `SlashingManager._verifyVotes`. fn compute_vote_digest( chain_id: u64, + verifying_contract: Address, e3_id: u64, accusation_id: FixedBytes<32>, voter: Address, @@ -419,10 +443,9 @@ fn compute_vote_digest( data_hash: FixedBytes<32>, ) -> FixedBytes<32> { let typehash = keccak256(VOTE_TYPEHASH_STR); - keccak256( + let struct_hash = keccak256( &( typehash, - U256::from(chain_id), U256::from(e3_id), accusation_id, voter, @@ -430,22 +453,39 @@ fn compute_vote_digest( data_hash, ) .abi_encode(), - ) + ); + let domain = compute_vote_domain_separator(chain_id, verifying_contract); + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(domain.as_ref()); + buf.extend_from_slice(struct_hash.as_ref()); + keccak256(&buf) } -/// Sign a vote and return `(voter_address, signature_bytes)`. +/// Sign a vote and return `(voter_address, signature_bytes)`. EIP-712 typed-data signature. fn sign_vote( signer: &PrivateKeySigner, chain_id: u64, + verifying_contract: Address, e3_id: u64, accusation_id: FixedBytes<32>, agrees: bool, data_hash: FixedBytes<32>, ) -> (Address, Bytes) { let voter = signer.address(); - let digest = compute_vote_digest(chain_id, e3_id, accusation_id, voter, agrees, data_hash); + let digest = compute_vote_digest( + chain_id, + verifying_contract, + e3_id, + accusation_id, + voter, + agrees, + data_hash, + ); + // EIP-712: sign the typed-data hash directly (no EIP-191 wrapping). let sig = signer - .sign_message_sync(digest.as_ref()) + .sign_hash_sync(&digest) .expect("vote signing should succeed"); (voter, Bytes::from(sig.as_bytes().to_vec())) } @@ -500,7 +540,7 @@ fn test_reason_for_proof_type_matches_solidity() { fn test_vote_typehash() { let expected: [u8; 32] = keccak256(VOTE_TYPEHASH_STR).into(); // Cross-check with the exact string the Solidity contract uses: - let sol_str = "AccusationVote(uint256 chainId,uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)"; + let sol_str = "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)"; let sol_hash: [u8; 32] = keccak256(sol_str).into(); assert_eq!( expected, sol_hash, @@ -508,10 +548,13 @@ fn test_vote_typehash() { ); } -/// Verifies vote digest computation matches manual abi.encode + keccak256. +/// Verifies vote digest computation matches the canonical EIP-712 typed-data hash. #[test] fn test_vote_digest_manual_computation() { let chain_id = 31337u64; + let verifying_contract: Address = "0x9999999999999999999999999999999999999999" + .parse() + .unwrap(); let e3_id = 42u64; let operator: Address = "0x1111111111111111111111111111111111111111" .parse() @@ -523,29 +566,44 @@ fn test_vote_digest_manual_computation() { let data_hash = FixedBytes::from([0xab; 32]); let accusation_id = compute_accusation_id(chain_id, e3_id, operator, proof_type); - let digest = compute_vote_digest(chain_id, e3_id, accusation_id, voter, true, data_hash); - - // Manual computation - let typehash = keccak256(VOTE_TYPEHASH_STR); - let encoded = ( - typehash, - U256::from(chain_id), - U256::from(e3_id), + let digest = compute_vote_digest( + chain_id, + verifying_contract, + e3_id, accusation_id, voter, true, data_hash, - ) - .abi_encode(); - let expected: FixedBytes<32> = keccak256(&encoded); + ); + + // Manual EIP-712 computation + let typehash = keccak256(VOTE_TYPEHASH_STR); + let struct_hash: FixedBytes<32> = keccak256( + &( + typehash, + U256::from(e3_id), + accusation_id, + voter, + true, + data_hash, + ) + .abi_encode(), + ); + let domain = compute_vote_domain_separator(chain_id, verifying_contract); + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(domain.as_ref()); + buf.extend_from_slice(struct_hash.as_ref()); + let expected: FixedBytes<32> = keccak256(&buf); assert_eq!( digest, expected, - "vote digest should match manual computation" + "vote digest should match canonical EIP-712 typed-data hash" ); } -/// Verifies vote sign/recover roundtrip. +/// Verifies vote sign/recover roundtrip (EIP-712, no EIP-191 wrapping). #[test] fn test_vote_signing_roundtrip() { let signer: PrivateKeySigner = @@ -553,6 +611,9 @@ fn test_vote_signing_roundtrip() { .parse() .unwrap(); let chain_id = 31337u64; + let verifying_contract: Address = "0x9999999999999999999999999999999999999999" + .parse() + .unwrap(); let e3_id = 42u64; let operator: Address = "0x1111111111111111111111111111111111111111" .parse() @@ -561,7 +622,15 @@ fn test_vote_signing_roundtrip() { let data_hash = FixedBytes::from([0xab; 32]); let accusation_id = compute_accusation_id(chain_id, e3_id, operator, proof_type); - let (voter, sig_bytes) = sign_vote(&signer, chain_id, e3_id, accusation_id, true, data_hash); + let (voter, sig_bytes) = sign_vote( + &signer, + chain_id, + verifying_contract, + e3_id, + accusation_id, + true, + data_hash, + ); assert_eq!( voter, @@ -569,12 +638,20 @@ fn test_vote_signing_roundtrip() { "voter should be the signer address" ); - // Verify recover - let digest = compute_vote_digest(chain_id, e3_id, accusation_id, voter, true, data_hash); + // Verify recover (raw prehash, no EIP-191 wrapping) + let digest = compute_vote_digest( + chain_id, + verifying_contract, + e3_id, + accusation_id, + voter, + true, + data_hash, + ); let sig = alloy::primitives::Signature::try_from(sig_bytes.as_ref()).expect("signature should parse"); let recovered = sig - .recover_address_from_msg(digest.as_slice()) + .recover_address_from_prehash(&digest) .expect("recovery should succeed"); assert_eq!( recovered, @@ -621,6 +698,9 @@ fn test_attestation_evidence_encoding() { let signer2: PrivateKeySigner = PrivateKeySigner::random(); let chain_id = 31337u64; + let verifying_contract: Address = "0x9999999999999999999999999999999999999999" + .parse() + .unwrap(); let e3_id = 1u64; let operator: Address = "0x1111111111111111111111111111111111111111" .parse() @@ -632,8 +712,24 @@ fn test_attestation_evidence_encoding() { // Evidence preimage must hash to dataHash on chain. let evidence_bytes = Bytes::from(vec![0xab, 0xcd, 0xef]); let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); - let (voter1, sig1) = sign_vote(&signer1, chain_id, e3_id, accusation_id, true, data_hash); - let (voter2, sig2) = sign_vote(&signer2, chain_id, e3_id, accusation_id, true, data_hash); + let (voter1, sig1) = sign_vote( + &signer1, + chain_id, + verifying_contract, + e3_id, + accusation_id, + true, + data_hash, + ); + let (voter2, sig2) = sign_vote( + &signer2, + chain_id, + verifying_contract, + e3_id, + accusation_id, + true, + data_hash, + ); let evidence = encode_attestation_evidence( proof_type, @@ -843,6 +939,7 @@ async fn test_onchain_valid_attestation_executes_slash() { let (v1, s1) = sign_vote( &voter_signer1, chain_id, + sm_addr, e3_id, accusation_id, true, @@ -851,6 +948,7 @@ async fn test_onchain_valid_attestation_executes_slash() { let (v2, s2) = sign_vote( &voter_signer2, chain_id, + sm_addr, e3_id, accusation_id, true, @@ -859,6 +957,7 @@ async fn test_onchain_valid_attestation_executes_slash() { let (v3, s3) = sign_vote( &voter_signer3, chain_id, + sm_addr, e3_id, accusation_id, true, @@ -1011,6 +1110,7 @@ async fn test_onchain_insufficient_attestations_reverts() { let (v1, s1) = sign_vote( &voter_signer1, chain_id, + sm_addr, e3_id, accusation_id, true, @@ -1126,6 +1226,7 @@ async fn test_onchain_voter_not_in_committee_reverts() { let (v_out, s_out) = sign_vote( &outsider_signer, chain_id, + sm_addr, e3_id, accusation_id, true, @@ -1244,6 +1345,7 @@ async fn test_onchain_invalid_vote_signature_reverts() { // Sign using impersonator's key but construct the digest for victim_signer's address let digest = compute_vote_digest( chain_id, + sm_addr, e3_id, accusation_id, victim_signer.address(), @@ -1251,7 +1353,7 @@ async fn test_onchain_invalid_vote_signature_reverts() { data_hash, ); let bad_sig = impersonator_signer - .sign_message_sync(digest.as_ref()) + .sign_hash_sync(&digest) .expect("signing should succeed"); // Build evidence claiming the vote is from victim_signer but signed by impersonator @@ -1373,6 +1475,7 @@ async fn test_onchain_duplicate_voter_reverts() { let (voter, sig) = sign_vote( &voter_signer, chain_id, + sm_addr, e3_id, accusation_id, true, @@ -1500,6 +1603,7 @@ async fn test_onchain_duplicate_evidence_reverts() { let (v1, s1) = sign_vote( &voter_signer1, chain_id, + sm_addr, e3_id, accusation_id, true, @@ -1508,6 +1612,7 @@ async fn test_onchain_duplicate_evidence_reverts() { let (v2, s2) = sign_vote( &voter_signer2, chain_id, + sm_addr, e3_id, accusation_id, true, diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 7afee35b1..88a2fd809 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -2564,5 +2564,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-5fae32d9d231e92c95043e312da64444ebf9ff4f" + "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index eb904ab08..a23ee28da 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -1005,5 +1005,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" + "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 020a32136..d5f339ccc 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -1186,5 +1186,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" + "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index 311215b95..49d654204 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2097,5 +2097,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-85f1f1bf0ad469963be1c17e486b3caefa5c35a2" + "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index bdc35df1d..e8c74071e 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -1006,5 +1006,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-5fae32d9d231e92c95043e312da64444ebf9ff4f" + "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index b5a771bc9..d3d8eeb58 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -1652,5 +1652,5 @@ }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-5fae32d9d231e92c95043e312da64444ebf9ff4f" + "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json index c4acccb04..a8dc197e9 100644 --- a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json +++ b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json @@ -1255,5 +1255,5 @@ ] }, "inputSourceName": "project/contracts/token/EnclaveTicketToken.sol", - "buildInfoId": "solc-0_8_28-ce9a7610acb826f83b656ea800eb1bae5dae3ce6" + "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 29c9a2073..401b12640 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -163,6 +163,9 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { ) public initializer { require(_owner != address(0), ZeroAddress()); + // Hold ownership transiently as `msg.sender` so the internal call to + // `setSortitionSubmissionWindow` (which is `onlyOwner`) succeeds, then + // transfer to the final `_owner` before returning. __Ownable_init(msg.sender); ciphernodes._init(TREE_DEPTH); setSortitionSubmissionWindow(_submissionWindow); @@ -487,7 +490,7 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { return false; } - _sortTopNodesByAscendingScore(c); + _sortTopNodesByAscendingAddress(c); c.stage = ICiphernodeRegistry.CommitteeStage.Finalized; c.activeCount = c.topNodes.length; @@ -855,7 +858,7 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { /// equivalent to numeric address-ascending for hex-encoded addresses). /// This also defines `party_id` = position in the address-sorted committee. /// @param c Committee storage reference - function _sortTopNodesByAscendingScore(Committee storage c) internal { + function _sortTopNodesByAscendingAddress(Committee storage c) internal { uint256 len = c.topNodes.length; for (uint256 i = 0; i < len; ++i) { for (uint256 j = i + 1; j < len; ++j) { diff --git a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol index e82b549ce..560c1cd29 100644 --- a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol +++ b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol @@ -10,9 +10,6 @@ import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import { - MessageHashUtils -} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import { ISlashingManager } from "../interfaces/ISlashingManager.sol"; import { IBondingRegistry } from "../interfaces/IBondingRegistry.sol"; import { ICiphernodeRegistry } from "../interfaces/ICiphernodeRegistry.sol"; @@ -84,20 +81,29 @@ contract SlashingManager is ISlashingManager, AccessControl { "ProofPayload(uint256 chainId,uint256 e3Id,uint256 proofType,bytes zkProof,bytes publicSignals)" ); - /// @notice EIP-712 style typehash for committee attestation votes. - /// @dev Must match `AccusationManager::vote_digest()` in `crates/zk-prover/src/actors/accusation_manager.rs`. - /// Includes chainId to prevent cross-chain replay and dataHash for equivocation detection. - /// @dev Signature scheme uses EIP-191 (`personal_sign`) wrapping rather than a - /// full EIP-712 domain separator with `verifyingContract`; canonicalising the - /// domain is tracked as a follow-up (requires plumbing the SlashingManager - /// address into off-chain signers). + /// @notice Canonical EIP-712 struct typehash for committee attestation votes. + /// @dev Must match `vote_struct_hash` layout in + /// `crates/zk-prover/src/actors/accusation_manager.rs`. + /// `chainId` and `verifyingContract` are bound by the EIP-712 domain, + /// not the struct, so they are not duplicated here. bytes32 public constant VOTE_TYPEHASH = keccak256( - "AccusationVote(uint256 chainId,uint256 e3Id," - "bytes32 accusationId,address voter," - "bool agrees,bytes32 dataHash)" + "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)" + ); + + /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. + bytes32 public constant EIP712_DOMAIN_TYPEHASH = + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); + /// @dev `keccak256("EnclaveSlashingManager")`. + bytes32 public constant DOMAIN_NAME_HASH = + keccak256(bytes("EnclaveSlashingManager")); + + /// @dev `keccak256("1")`. + bytes32 public constant DOMAIN_VERSION_HASH = keccak256(bytes("1")); + // ====================== // Modifiers // ====================== @@ -252,9 +258,12 @@ contract SlashingManager is ISlashingManager, AccessControl { /// `abi.encode(uint256 proofType, /// address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures, /// bytes evidence)` - /// Each voter must sign via `personal_sign`/`signMessage()` (EIP-191 prefixed): - /// `personal_sign(keccak256(abi.encode(VOTE_TYPEHASH, - /// block.chainid, e3Id, accusationId, voter, agrees, dataHash)))` + /// Each voter must sign the EIP-712 typed-data hash: + /// `keccak256("\x19\x01" || domainSeparator || structHash)` where + /// `domainSeparator = keccak256(abi.encode(EIP712_DOMAIN_TYPEHASH, + /// DOMAIN_NAME_HASH, DOMAIN_VERSION_HASH, chainId, address(this)))` + /// and `structHash = keccak256(abi.encode(VOTE_TYPEHASH, + /// e3Id, accusationId, voter, agrees, dataHash))` /// where `accusationId = keccak256(abi.encodePacked(block.chainid, e3Id, operator, proofType))` /// and `evidence` is the preimage of every voter's `dataHash`, i.e. /// `keccak256(evidence) == dataHashes[i]` for all i. @@ -492,6 +501,15 @@ contract SlashingManager is ISlashingManager, AccessControl { bytes32 accusationId = keccak256( abi.encodePacked(block.chainid, v.e3Id, v.operator, v.proofType) ); + bytes32 domainSeparator = keccak256( + abi.encode( + EIP712_DOMAIN_TYPEHASH, + DOMAIN_NAME_HASH, + DOMAIN_VERSION_HASH, + block.chainid, + address(this) + ) + ); address prevVoter = address(0); uint256 numVotes = v.voters.length; @@ -509,21 +527,21 @@ contract SlashingManager is ISlashingManager, AccessControl { VoterNotInCommittee() ); - bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash( - keccak256( - abi.encode( - VOTE_TYPEHASH, - block.chainid, - v.e3Id, - accusationId, - voter, - v.agrees[i], - v.dataHashes[i] - ) + bytes32 structHash = keccak256( + abi.encode( + VOTE_TYPEHASH, + v.e3Id, + accusationId, + voter, + v.agrees[i], + v.dataHashes[i] ) ); + bytes32 digest = keccak256( + abi.encodePacked("\x19\x01", domainSeparator, structHash) + ); require( - ECDSA.recover(ethSignedHash, v.signatures[i]) == voter, + ECDSA.recover(digest, v.signatures[i]) == voter, InvalidVoteSignature() ); } diff --git a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol index 4ff4a6355..0f7f4ba30 100644 --- a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol @@ -50,6 +50,18 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { ) private pure returns (BundleData memory data) { (, data.publicInputs) = abi.decode(proof, (bytes, bytes32[])); data.h = _honestPartyCount(data.publicInputs); + // Defense in depth: require the public-inputs `partyId` slots + // (`publicInputs[2..2+h]`) to be strictly ascending. The zk circuit + // already enforces this, but rejecting duplicates here prevents two + // bindings from resolving to the same slot in `_partySlot` (which + // would silently overwrite each other in `partyIdsOut` etc.). + for (uint256 k = 1; k < data.h; k++) { + require( + uint256(data.publicInputs[2 + k]) > + uint256(data.publicInputs[2 + k - 1]), + ICiphernodeRegistry.InvalidFoldAttestation() + ); + } (data.attestations, data.bindings) = abi.decode( dkgAttestationBundle, ( @@ -135,6 +147,10 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { ICiphernodeRegistry.InvalidFoldAttestation() ); h = (publicInputs.length - 6) / 3; + // Defense in depth: the BFV pk-verifier already rejects `h == 0`, but + // a zero-honest-party proof would otherwise pass this verifier with no + // attestations to check and write empty anchors to the registry. + require(h > 0, ICiphernodeRegistry.InvalidFoldAttestation()); } function _verifyBinding( diff --git a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol index c27bfef4e..8f8d5f63f 100644 --- a/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol @@ -8,7 +8,7 @@ pragma solidity >=0.8.21; uint256 constant N = 2097152; uint256 constant LOG_N = 21; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 31; -uint256 constant VK_HASH = 0x14b4fe3a693ffd187b26ec6e5d7114f1b2db31e03c8ed3f4c248eeb21b6b5d00; +uint256 constant VK_HASH = 0x1c0a60837c2a1d7cc5e62a5a531d6d1e4e9685388506a78f7c0bb201eef5ad96; library HonkVerificationKey { function loadVerificationKey() internal @@ -21,50 +21,50 @@ library HonkVerificationKey { publicInputsSize: uint256(31), ql: Honk.G1Point({ x: uint256( - 0x0958727f8e6b167c3ce01ece1a2577e6070fd5908d23fe59eda6a35c2956b826 + 0x1d906d083872617485399605e6b8d7e0eea0a10f71271b8c59adf24cc8339be7 ), y: uint256( - 0x2bc5420d03d0c2416bcd46793cf3588dfac89124cd6ab4f40a899e6ac531dd27 + 0x26cad07a2734aa9aee4ce7b358d3043b357b9c91a96529d54f52520050342e9a ) }), qr: Honk.G1Point({ x: uint256( - 0x0b6dd3cf30a9e25feaf544219ded9dc02437e61bb1e306bfff98091b74b510d8 + 0x246cfefd937a61df24c91a5f3fe31de8e7ce3f17837637c0e1f55e5889f58c77 ), y: uint256( - 0x02225b1d8f932da744fa83d00b25eafb55b2cff0a60648f5f41e0fa89212c431 + 0x11bed90de8b7c6acb3b363d70850528c89b62b73d9f9b83a2ca30bce346bad94 ) }), qo: Honk.G1Point({ x: uint256( - 0x27bd5529d88a687fe0cbd2f1dc65d13b367554441d8301281f1ef9b36a7eb1ce + 0x0e932b810103be7429befc3ade0b3c06617473d69778daad01d4b62e6ea89507 ), y: uint256( - 0x12349e015d7323e632955a532e07839452f81fbda0a6e47c101d0888fa5f69bd + 0x1b808239e3a1eca0011029525cc8fbfa471341d8d94df76b1c17718216f5eeda ) }), q4: Honk.G1Point({ x: uint256( - 0x14cb386a7056a199dbb60bda8be9f6bda6248b825eaab0176f778c24631d334b + 0x0c18563d13b6db7effc22340fc74a2e7b2f9dbb0b42d3f0acc47331f3558dae9 ), y: uint256( - 0x24d8992a7e273a55a42ac837d3ae4eb9cb45776670202138a452c8b71fdb0f6a + 0x2ff93536486524c1c58a883b55284b278c7efdafc5b41808d88fce3243b11d69 ) }), qm: Honk.G1Point({ x: uint256( - 0x1aa3cd6a7d3eb455d1a2130eb50220c4936b5406d2b01ca2741238ca207524f6 + 0x104aa9b4476927b9c3df51c532b1cae7b87f5b7ab9373da43777be83108dbca7 ), y: uint256( - 0x1778501afed9c76de9eaf7a4d9bb2bb66eb6e6867e77d76dc49dbe8b5f50b789 + 0x1cd279d089fb417574bdfff9320b8ef459196f7841d15f10ef03e78999520fe6 ) }), qc: Honk.G1Point({ x: uint256( - 0x01ee045046d69b516f31758adb206ab84f4816e7c2290326ceb5f50612dd7619 + 0x2749c7c3188e135b4baacb08abda70578c43ba2793c2e376adebc8f5e092a1cb ), y: uint256( - 0x19474d09206a00fb6f8eed0f836b3f0832338dd7292b5673ebb3b17b0ce618ea + 0x02e6a616a7cd3b2c2107c830a18f766f558c5a56e2fe560de4216cb42bbe4095 ) }), qLookup: Honk.G1Point({ @@ -77,90 +77,90 @@ library HonkVerificationKey { }), qArith: Honk.G1Point({ x: uint256( - 0x2fef59c5aa902c6d74f45fced7a3248ae6eb0381dfa7bfd3a691ead4114e3dec + 0x2efb699f4c4dbbd9ec2552bfd41f42f8a5e958cc50ff9dc953be33140272458a ), y: uint256( - 0x2b3db764a959504ed3f9c1475b81cdad8af835103d75df014e3d62c45a1a7aeb + 0x06c6d7d7d0a685224f444ac30d3fa678760361d759abc9e7a229ea8979703e61 ) }), qDeltaRange: Honk.G1Point({ x: uint256( - 0x0caf4a2b51a053c7fd0aade49842b0000d8e7ef09a6155af99cbddb3a01ca3de + 0x019002e97cd41dd882e373b2bc79c3cad6a400244ff1e80aae06c0b1186b1e20 ), y: uint256( - 0x0f57bf82d0434ab12375af295208858e5dca1801de11adee2b42b0a62c3b5f73 + 0x1fef3eed64626bfd4a793a5330d1eebc6af338a1814c237fae0dc532abe6203a ) }), qElliptic: Honk.G1Point({ x: uint256( - 0x0099ea3f7f7d572dd9a18323b5958cb176ccfa6899b2a1add1d993501b5067b6 + 0x2d867487b60acfaa537feeae0185cfbcb84315e1f9d8eb13e33fcee51e35cc4d ), y: uint256( - 0x0177fce19b1b04dfbb126b714ddd83dcac2e550d6f141b87cbf91a5c1497ad44 + 0x231c0fe3f3de4990752e61ac8897498223d1cc527808376a7ab250c61b48e170 ) }), qMemory: Honk.G1Point({ x: uint256( - 0x1caef229c2fe5b1a72a94305a53e248caa15e89b31eafa3bc3606ac603f15b62 + 0x1954e635e980037fb7bc3c25c450d12a9a19837e01e3f1d479d51728bb70af6b ), y: uint256( - 0x2414fe66f1180153b95cb96ed95b83e0a4b6693ac85246d11b035efc626a4259 + 0x2ce76f6c6fe16633bf7c675281759908490fed6ff2829e8997513eb4fcf56f53 ) }), qNnf: Honk.G1Point({ x: uint256( - 0x0309e05930f062f6ffeff4b245730639d250b8a59317834961cabfd18621c1b6 + 0x077d3f2336940aec76438f6c30edddd2e751c56a72ffaf64e9ef476f524daa2c ), y: uint256( - 0x221fb2636f3710e76b2fae7fcf76b5948395d9eaf4bf6b996650e0aa787b7cd1 + 0x04b61eb1f9b0837bcc0714bb3c00c190349dacb7ccaab86a6027b910dd2309e0 ) }), qPoseidon2External: Honk.G1Point({ x: uint256( - 0x2b0f1bbabd6f95ad5890e4516679fb74af54a5a45f9ae28ded1e73725aaa40be + 0x24a3cef3397bef0d207ce03d64d77ea0a7dabde143471d98cd7360b7685a90b7 ), y: uint256( - 0x0c37425f8aa86560feaa78caa37b3e9682ee2a87a0cb7b8dbda9769d6f45ad90 + 0x165894aaaf725b36f72e71880ff23c78ffa1b757304b00b94c56f7e9886936ce ) }), qPoseidon2Internal: Honk.G1Point({ x: uint256( - 0x2787aa1477492c712da27b35f4115efb891fb30572fe45b9d81ab4ec46f1a27e + 0x24bf2806f2e4fdb9a4728a47a888038a8602f1a564cce5439d8a0c89eb04b351 ), y: uint256( - 0x099006d3e16ae9253d3acb3c5e53ca9c55d8efc79d74d2cd5cccb2423d9576c4 + 0x21002a27f9d4cb5b284ad2b3c83e2956e1ecdef6ab8ad07b085a429f82374a77 ) }), s1: Honk.G1Point({ x: uint256( - 0x23b78d646343f8056a8482427a343dee641eaab6efb05d458744f5a7a4cfb830 + 0x1b1656cadd0b2ab6f8a6e5e22d0dec012d95f87e22432d4acfa44d0272596189 ), y: uint256( - 0x2452e4c29baa3e0a51ff9cda1fc7e00f6522f11dfe40f093c37fb526ad7e6c42 + 0x283c907c7278fa3fce6eeadeaaa9c48f7ab8211c94882f851c9fdcabdbac1bdc ) }), s2: Honk.G1Point({ x: uint256( - 0x1ae7ba411c345ac64c36bba0eb84718176724aa2f3df0205d72f9733c1a18b7e + 0x23f558a747590ffc98f1cec61982a939c17078fb8597d196c5f71436f6135393 ), y: uint256( - 0x0e5e836e64262266e7133041f118679aa649b4b5511882161fdf2ecd3371f925 + 0x0bb3abcbf063633ba4de4a51dec092e6f79c64cf48340c2bb149bd340cc95e9c ) }), s3: Honk.G1Point({ x: uint256( - 0x185156ae67d79b65d13c0676c2786c765dc0de33a89edf9ca9f3efaedc705374 + 0x120874be1571c176bdb51bf3357b34f678084ba91f8d644019ce9cf69f819537 ), y: uint256( - 0x0dc9bfb2bfc6468800b18af1a1753eaec1c55bb133e4155b590f16e7f1fdec32 + 0x0b28eef919110795820b3818e697556033489a739c2056801c36b3e15b7a16ea ) }), s4: Honk.G1Point({ x: uint256( - 0x1b173e123091e39c31eb2203067d1d8c099c6e90b688c47aa25afc4d97b6565d + 0x2face01c0f73016a476cdae4e8825f315ed329847cf15c2f1c46a270cf1cdbab ), y: uint256( - 0x1ec380d3086d115a0316784be7d05ed9c54f0465d5c0f404dfea3e52c7d41ee7 + 0x09d51e6255090c8a48ef74e1d2f20a6becf2b22811e77308559545471bbf9f92 ) }), t1: Honk.G1Point({ @@ -197,34 +197,34 @@ library HonkVerificationKey { }), id1: Honk.G1Point({ x: uint256( - 0x2bc73538ea9e8eeb7917d58ee1d92c61677cfa9d0344a8f4c97bc29ce4018723 + 0x00efe1593a923558e7779f10e4753794a59ad0dcc02df6790b72346175b1b7b9 ), y: uint256( - 0x2ad1b2af81c91ce2016c5cf5c5cb1dba6824f29029ebab60f8543d2c521bb5d9 + 0x28b0744e7ea51d27a2624e7ffe4ccae8f3c9c6350131555ceedb42078a229a3c ) }), id2: Honk.G1Point({ x: uint256( - 0x2824912b8ffffdac70557c216b9d4472f22d0e571124f0d555083cdbaa0d888b + 0x0f28ce7d149491697d292002785ff24f6089b458ea01234e4af8eda65b9dd266 ), y: uint256( - 0x1b2ee76cc4d05b22c6775e175ca5eb28f0b3d735b617f1cc53766b6b223183f1 + 0x24519d3dab7ba27bcf50279756292c7052edff4183446b2ae85617e6dff01883 ) }), id3: Honk.G1Point({ x: uint256( - 0x18b23eb8e509784f9c56ab322fffcb531bb6616e2e4f1f77e9f4a2fe5fc46772 + 0x27ae1958d5100a4fbe5ae016fdac9ea54c5c4172cc798a38d3f8730c5f481a5d ), y: uint256( - 0x0d0fb14c991420805a1d310f9605dc96ea838cf12f2f2e264d59b5f6b8ad6d8d + 0x2e28100a9463b43a586fb70dfe15c841ddbd30703149bd5d9fb7273d520554c5 ) }), id4: Honk.G1Point({ x: uint256( - 0x28a8005794ce64e011078d96be8f37f6337520c10cb11f977a3c7a6731ef7aec + 0x072921e4c634152a135b6fcd0c5cadfa66f780284592d1f3262fa04128f7ba6a ), y: uint256( - 0x124a06f7822d418a4a0ceba357738929490918f311d898c441e3198c56e946b8 + 0x0bfd2eec92d6aad31ca352447ca9a9e69eb7a2465f9741f67ea75c5622d14e02 ) }), lagrangeFirst: Honk.G1Point({ @@ -237,10 +237,10 @@ library HonkVerificationKey { }), lagrangeLast: Honk.G1Point({ x: uint256( - 0x0c42e54693f868900a2637f8a1321658762b1458c97b238ffed9617b88d9415e + 0x1f248647726750901d3e276b8a1d386600b913a3924d5bbc43cb896f40024400 ), y: uint256( - 0x114f2696f738a18b2f1a4a71a3121ac45feab18803d5e22a3c92c1cbe3ae4093 + 0x20c67ede3b2a84a692458ecf65ad55a687027c57f4eca98eb419ea3b79f669c1 ) }) }); diff --git a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts index b86a67c11..e2ae2bed3 100644 --- a/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts +++ b/packages/enclave-contracts/test/E3Lifecycle/E3Integration.spec.ts @@ -770,6 +770,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), ); await slashingManager.proposeSlash( @@ -859,6 +860,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), ); await slashingManager.proposeSlash( 0, @@ -1413,6 +1415,7 @@ describe("E3 Integration - Refund/Timeout Mechanism", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), ); await slashingManager.proposeSlash( 0, diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index 88fcb3c3f..7588818d3 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -412,6 +412,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, op1Address, + await slashingManager.getAddress(), ); const tx = await slashingManager.proposeSlash(0, op1Address, proof); @@ -459,6 +460,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), ); await slashingManager.proposeSlash( 0, @@ -530,6 +532,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), 0, 31337, ethers.keccak256(evidence1), @@ -602,6 +605,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), 0, 31337, ethers.keccak256(ev1), @@ -623,6 +627,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), 7, // C6ThresholdShareDecryption — different proofType 31337, ethers.keccak256(ev2), @@ -681,6 +686,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), ); await slashingManager.proposeSlash( 0, @@ -740,6 +746,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), 0, 31337, ethers.keccak256(evExpel1), @@ -758,6 +765,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator3, operator4], 0, await operator2.getAddress(), + await slashingManager.getAddress(), 0, 31337, ethers.keccak256(evExpel2), @@ -826,6 +834,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), ); await slashingManager.proposeSlash( 0, @@ -891,6 +900,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), 0, 31337, ethers.keccak256(evExpelOp1), @@ -909,6 +919,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator3, operator4], 0, await operator2.getAddress(), + await slashingManager.getAddress(), 0, 31337, ethers.keccak256(evExpelOp2), @@ -938,6 +949,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator4], 0, await operator3.getAddress(), + await slashingManager.getAddress(), 0, 31337, ethers.keccak256(ethers.toUtf8Bytes("expel-op3")), @@ -981,6 +993,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { [operator2, operator3], 0, await operator1.getAddress(), + await slashingManager.getAddress(), ); const op1Addr = await operator1.getAddress(); const tx = await slashingManager.proposeSlash(0, op1Addr, proof); diff --git a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts index 9880f7c10..123000845 100644 --- a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts +++ b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts @@ -573,6 +573,7 @@ describe("SlashingManager", function () { [voter1, voter2], e3Id, operatorAddress, + await slashingManager.getAddress(), ); // Anyone can submit the signed attestation evidence (permissionless for Lane A) @@ -628,6 +629,7 @@ describe("SlashingManager", function () { [voter1], // only 1 voter, need 2 0, operatorAddress, + await slashingManager.getAddress(), ); await expect( slashingManager @@ -782,6 +784,7 @@ describe("SlashingManager", function () { [voter1, voter2], // voter2 is NOT in committee 0, operatorAddress, + await slashingManager.getAddress(), ); await expect( slashingManager @@ -876,6 +879,7 @@ describe("SlashingManager", function () { [voter1, voter2], 0, operatorAddress, + await slashingManager.getAddress(), ); await slashingManager .connect(proposer) @@ -922,6 +926,7 @@ describe("SlashingManager", function () { [voter1, voter2], 0, operatorAddress, + await slashingManager.getAddress(), ); await slashingManager .connect(proposer) @@ -933,6 +938,7 @@ describe("SlashingManager", function () { [voter1, voter2], 1, operatorAddress, + await slashingManager.getAddress(), ); await slashingManager .connect(proposer) @@ -968,6 +974,7 @@ describe("SlashingManager", function () { [voter1, voter2], 0, operatorAddress, + await slashingManager.getAddress(), 1, // proofType=1 maps to REASON_PT_1 (ban policy) ); await slashingManager @@ -1105,6 +1112,7 @@ describe("SlashingManager", function () { [voter1, voter2], 0, operatorAddress, + await slashingManager.getAddress(), ); await slashingManager .connect(proposer) @@ -1264,6 +1272,7 @@ describe("SlashingManager", function () { [voter1, voter2], 0, operatorAddress, + await slashingManager.getAddress(), ); await slashingManager .connect(proposer) @@ -1531,6 +1540,7 @@ describe("SlashingManager", function () { [voter1, voter2], 0, operatorAddress, + await slashingManager.getAddress(), ); await slashingManager .connect(proposer) diff --git a/packages/enclave-contracts/test/fixtures/attestation.ts b/packages/enclave-contracts/test/fixtures/attestation.ts index 93327c966..25a6a5093 100644 --- a/packages/enclave-contracts/test/fixtures/attestation.ts +++ b/packages/enclave-contracts/test/fixtures/attestation.ts @@ -11,15 +11,20 @@ const { ethers } = await network.connect(); const abiCoder = ethers.AbiCoder.defaultAbiCoder(); +/// Canonical EIP-712 struct typehash for vote sigs (matches SlashingManager.VOTE_TYPEHASH). export const VOTE_TYPEHASH = ethers.keccak256( ethers.toUtf8Bytes( - "AccusationVote(uint256 chainId,uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)", + "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)", ), ); +const VOTE_DOMAIN_NAME = "EnclaveSlashingManager"; +const VOTE_DOMAIN_VERSION = "1"; + /** * Helper to create signed committee attestation evidence for Lane A. - * Each voter signs a VOTE_TYPEHASH-structured digest via personal_sign (EIP-191). + * Each voter signs the canonical EIP-712 typed-data hash matching + * `SlashingManager._verifyVotes`. * Returns * abi.encode(proofType, voters, agrees, dataHashes, signatures, evidence) * with voters sorted ascending by address. @@ -28,11 +33,15 @@ export const VOTE_TYPEHASH = ethers.keccak256( * `keccak256(evidence) == dataHash`). If `evidence` is provided, `dataHash` is * derived from it automatically; pass `dataHash` explicitly only to test the * keccak-binding check itself. + * + * `slashingManager` is the deployed SlashingManager address — used as the + * EIP-712 `verifyingContract`. */ export async function signAndEncodeAttestation( voterSigners: Signer[], e3Id: number, operator: string, + slashingManager: string, proofType: number = 0, chainId: number = 31337, dataHash?: string, @@ -69,6 +78,22 @@ export async function signAndEncodeAttestation( const dataHashes: string[] = []; const signatures: string[] = []; + const domain = { + name: VOTE_DOMAIN_NAME, + version: VOTE_DOMAIN_VERSION, + chainId, + verifyingContract: slashingManager, + }; + const types = { + AccusationVote: [ + { name: "e3Id", type: "uint256" }, + { name: "accusationId", type: "bytes32" }, + { name: "voter", type: "address" }, + { name: "agrees", type: "bool" }, + { name: "dataHash", type: "bytes32" }, + ], + }; + for (let i = 0; i < signersWithAddrs.length; i++) { const { signer, @@ -82,29 +107,14 @@ export async function signAndEncodeAttestation( agrees.push(voteAgrees); dataHashes.push(dataHash); - const messageHash = ethers.keccak256( - abiCoder.encode( - [ - "bytes32", - "uint256", - "uint256", - "bytes32", - "address", - "bool", - "bytes32", - ], - [ - VOTE_TYPEHASH, - chainId, - e3Id, - accusationId, - voterAddress, - voteAgrees, - dataHash, - ], - ), - ); - const signature = await signer.signMessage(ethers.getBytes(messageHash)); + const value = { + e3Id, + accusationId, + voter: voterAddress, + agrees: voteAgrees, + dataHash, + }; + const signature = await signer.signTypedData(domain, types, value); signatures.push(signature); } diff --git a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json index ea446f187..23b688cd1 100644 --- a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json @@ -1,10 +1,10 @@ { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000ca0bfc9448e9ef16c000000000000000000000000000000000000000000000002c6fc73ad221ed124000000000000000000000000000000000000000000000008224c7cdccbb18af40000000000000000000000000000000000000000000000000001877d7b0fe868000000000000000000000000000000000000000000000004ffaa9844e438d4d300000000000000000000000000000000000000000000000ac1d3924637ac840700000000000000000000000000000000000000000000000b23147b92159485ad0000000000000000000000000000000000000000000000000002e033f6bc040300000000000000000000000000000000000000000000000768068eda5eb7ec45000000000000000000000000000000000000000000000006b320ac7697a7d933000000000000000000000000000000000000000000000003ca3c43312f4a1a730000000000000000000000000000000000000000000000000000b90e9ebd4813000000000000000000000000000000000000000000000002d7efb8bc1720f5e4000000000000000000000000000000000000000000000003f44e6bbb5f7dd5e000000000000000000000000000000000000000000000000d73b5021fc0617d170000000000000000000000000000000000000000000000000002138f4eea500814ddc04dff7aade6906eaa66607c4f4fc4b7fad8d7d6dc1781b1ccb5824ae1460e35805f5cf672d03d0c6e86292f9270e308666e0a68e54ba34c48c7d1c1171e08ab78f3b3a380edceaf2be1332bb0f6970b24f889216fb52b96903c3672078e2a2d8c18c4b84f1ee541265621917acd4fce05eec99dc817ef9a1b30df545bdf2ffe8839de28e724ff17364a02a9190034ee4093d0771fe513866af5eee3e16c2640fd2eed50030bcc39cdfc2089b3af2b2fc2cf47c558c544d264cb316924fb06cabc1c64e480876c03a96dd26e2ec80e2a1fe4a060eba855017ac571e27bf226f2d314aa75307227826f8c8cc3558237c0c85427b4053190b5a66b8518499a04c8896e47bde11d94d474f7eb7e24a697ff050a323e51605c0a2f3795b43a55216ecd8b7dbdd337ec0eed71fac7dca88403ce3aad06ca8e1f5804dbf2dbfeea2d01dd0ea69cbd98b44cd4163166c47696b0cb64815f84169ca91ff184bc2aa703873167206ba8215672551c6c39711972e0fcb6171286004706f3f61d7c56d50f286c3eb1632be8786c44dc777090a9f3b2528a91259674a958b8287d9e6e7902eda561c95e35960e5fc1a918c47214a8a3fd83999188e9cf8c4bb3af86a867072be6dc0e4cf33e535bb85259a027f564bfcb50f2633630222848a6fbc30bc81f15de7cce451611df563628ebf63c866506617a24c47ee9943936bedf6006d403e517ea950c046b99c76b6a2f049de41634d85d23ec7fe432a42c24131a81e41e87bc342ff0f4a94a49a0e42bc24fab0ad8569dbe45de50452d2f1518f6964d106282a56209e9549419539fa23a7a21d5adb558af735bb87d547053b4b077b328e1943bbf60c5f59f31a56dbd1bf144efa369f33bb0c50f7e45ebc416e850d2241b4c910649052f98c615c093e763eb742f1cd429f36c4824b3f875f18c45581825b2a7c498228972f28cc96b5c90ee1c1dbbbc46d9ffae4973bccf711fca6d17a07d52ec5c6aed0fc3e9a064c37ade09de5dcae4e637f46271f943e8933c50061892e9ad08e8bff03cd7eddfc9ffada674bb27d9867e628d90ef6e9fd19ee92222e6a3072e0948ff4d178389a49a145e68efdbf306e35874e3ee27452ff17028fbcba83976dfd49e75c8bad82fb9339e77aa7fb76af7670b90e084210ebcc223d988bb703c4a113f7131e52a583d944668b72c08fcc4f552a6927dc3c7333b06b41b4b298fb5caa9170db380fe81195d0da8c38d6de7c337c52e5f024f046526d3b8bf4befc00cc172fde6e835ebe761c7af500c1a3de202df707e91201f7a02d38fa8b9456f6777eb006c1834afae72b3e500ffcea21a36b82e83a704da1e1863aaf8b58a8662f0dcb6fe6fd3e35bcb4bb922291315bcdd7d2f00ccc7db3d20e4d6112826184c37f83fef8bbe495aa3976e2e8c334b967ce4fc0a94d26503189d92a2f7b89fea1bcef0c5ae1147d833e4ac1d52120b36cb7d10619527140b27a521e78a3d4075e127c21300306256edc76d094502551553efcead0caec3b1097621f0a820ca04263bd57e7afa49cfb92a50eeffe2ac26f7502a496adfdf8e23d183479522e4fd838ee09a8fd50fec4a22103536fb118a251999b16042603d025ddd4670df746903205ec04aaa1c131a8565298996d2ef8ae601c995ee0e1c27b2ee13e4555206067c20ee5e054c793933fad112f9cfb4edefa8b6a5cccc8a01fdf4cb41e6d9bf4216246445428daa66e8263a65b79139ab1011ef9a150b9b1a40b8798c1e4e008ec02795bbf95bdd7a531a94ae8df852e92b98e7766fa3ad0c79d387b21791f763ca553d7d225632bcf58e60c6c608ee1443f98f979f9d3603afa7aef8fb5c103ca78c01bcc6917d4f1e0aa09bf3ea417c51c1c3f9ea2b902151931b21c9d25e574019fb092e03c40399b0b8fad6e3347f5961674c366450049609cb8eae6ff4e987806a36851ec132675dfdd390f61fc185b808b31af6ab220359a2710339887cdb032a26cee0dc574e33c1651f7e8b746d016f39fff2c605cb5e7f0b7a839277f904d2bf506ecc02f74be5baa88793d3265700bf8801312174144bf5372435bebb6c3af3d31bd15519858190765d99eb03caa1b516aa3a219db366834e6cf2d8dd47bafc50fce17f23a9556dc30699f727f09edecee94d252a7c4d5498c27a3915b7ac677625ac6829b5777a2dd234491d87168bccbf5301b9221ff82f1648b3b24d2e107329c0a910034296426f76ccc9941c1267df6d14d180edf7ff58f8aafc9fddc2b6a9a641e608c5d6ee03ccd615b645b17775502ac1b479e0010f878c96dcfb913639debcc8dc163faa63994d6fe4299db056f125cbabf7fd62bc792e6505b14e5849ef4506bb43babd4cd46e9df6d402c7e4de2e213bf9ca80aaca06b1baeb5554cce579df7cfad676200cfd2518592c92c6810728dcbf5a308018bdd6a9d0d6a3249cd59a8725da15de0a96a899f510c5407711b0753955e733c32aa3b780c292dcc4a8a24ff2f7bb224370ad0b0c6fca1f601947e99c3cbb54eefe69676eb12aaa3473410966221a4980511010e6f3212c281aae2b27f42ea11fb627fa4c854828ae3d1bb27551fb5ec75b54e342f5cef48f0d592aca51491f88b890e7dd0aaeeb290d165a763aa3b7f3b34ff65fa5a05f8f014f6b3daab5bcefdc03d0aa1f38464f5b0791d8e1f379669b72f548d9afe3490cbeeb0626f19b784265812c1f6161978d27f9e9c0349f028fe3b71197ef130c2dcf2eb7d5edb0b18a4194616eb35c7d614e26fcab690613d9757b323d94867d0da5034a534f65584cce55bd9c5119c20d2b8431ace83236e90ccb7be0c387b11b3c916229a3bd8603bfcb9e293b7c9a2363b898382d57eed13d5ed795066b07218dab59a3ffb999172a0025c43d200d12d8a155128c4db831c24560f654e43f1a27266db9ad1d94700efab02e2f0d39c6e2a84f02523fb12d290ba50e9a1a9805b38dca8bc7363b838f11fc5fcc4a39acc6c72112807ce045be538f43e9f27c01e75d05c14f7596940ec764feb95e2e28f10390a0e74e5cb3f16140127cfa6a156fa560a3c6e13592c8cb5acce08db1c9874a24e8a9ec69e453fa9f1384d46b1f263787814072fc03ea72b8abc134a205c058e5e9b963cf4b4b043993a01abe01f4245be8b9d7f9f127e5c248ac6c5b5dd7b3454e07ded8374360d40d75a39d1d609f99c2bcec53998e407d157b88698bf2b4c830874dfa537b5660303e798d095fe35d6c3ccbbbd131305128dfc78d99bd02faaca0f03250b0ba2cf8f859000afa115aacb761780b4ccf0eabb203038ea0ba410b2444e91ae6a10d0a729763234c408b6cbaf328d74d6be6bfdd4b4bdb74bde0e49da02911bbac860f7b1ecd27981f9ba1b18d35d4db16012579d470cba0e5e117421ac0da980fb45ea23f9b1f4bfded7640d9a41767a3819ffed3d0b1ccff98ad78f63cb4548b6c487e3fa90a8e68436d8db97480afb645de0e76939f6aec3a9183ccc113ae639c4f445b4c161e47279af3abe35b80d6a971ae876e0bcdf15ddc455da007ed56c493de38182b07e1f76681a2fa9196f45c3d25f98606688b55ea6336e19638711ff00d788f16bab3baf7545061ef03f59fd3b3c69a5da63683b801868689aa56c890cba7640b3ef1806aae00be03157081b188c4bfcc045ddd65d1364bbd2f73d03d5eccff18cbfd02fef55f8259a3abb1cf3c80dae34193e155ce2d02117b2281181acad525abfcef8f617e08f3264d0cc3676b330037bef0c76d2be781d9109130dae3a2064dde40ea9103327dc858625e5c5f43636aeadfbbf08b766607aea64758115d12f3a3ccaa680c8bac7c90675b26db2aa4e437820f8843f613f9f1bfd8cb4a431bdab7ae3f2815251e6440f4575397b1362413dc0d908ac8035d7e0dc154462102269ce752caf1c0e965c80463937edade5f71dbb16559824a082fd53c65483e06fb749bec6adfe9659772b8db6d26541bf074d751096ad81e99c52d7ef4102c2646c0a220aa8b25cc7fff384b029e841c5e1d1a9663e247c9fb35838a44322b00b86c4f53843909921594d8772dd67f6fcd57b555453ec0e6a2914fb82b4f9e2fa795c99591a02f6cddcbeeff3995a26eff6d038b7c063ce6b7e95f591130710c9a83eb1d4092d30b6e3a0db2643d049eddbb6486ca3ff613045343ae99a6af052b868c84142535fcf42cd31d9c039d599e1492cce99d4dd8439330b0a2077f165fc7e1fb0a27e24eac75faefb6ebb8b825e535423f5663b09f7b157205c0302151f296af0b1d5734e1e2a8eb3cd397a8f7e9cd015e333ce73c33df4bd49e860bda526af203f82dc6b615c2ebbbaa3b99a13e45f231c133bdc755807b03311123d395bfe1a13422c38063cd2f8cc6604a3ffbad19e5bbd5776668f5b907c3f72150da121fc376b7231173de23fb4b6d2edc5989dd2e82ae695cce929259aac229968d3d266b9913cf2d577474ccbda946ddc4ad52cab0388806f364246982402ca8b651f56a0f593424f4511a776152de94b2d5e040f23eb6f50d03029a0731056d6bb00954510884059b04e580005f131b335f45dd627242e573447d24ca9317ebaa00cee1d6cd54e10693d4656d19191ea9d63f1fb58989b317a7bb4f173f02154a9ad7593c3f48daa36a36deaf60a363365cadefcb3cd7f7eedf78e07817168d8e60ae9532d27b0eb422367876aa2840738a06d8487727fb74a6e75e44f6045b19a874d8bbd97efbaf376dd343648da57e8da19a975e3c715af62bcd12cf1b40e16223afd640866cefb3d7dba53e2e6a306634692d558fdf6ca8190683c8241d67526488d47450901eee756a9ce807cf18f54e62cc823f5d47b4e3af46ca1f3b94e88bd4115fd91451db59a8e8dc0c111024a2b38af1deb3854d2994887b1037817024ab3081acfeff6b50282ba402d85b56966899fb39a6c01df6771f33044b41a29308e89a75b06b6627d85cb028d191f46bfc52df2723360f52662e99087a5737c61ee99f7cd057bf2e18689f5b747595ff1199ae6798b0499c8d75e113362ac3d589042b46807e6fbf16b6e40c8f94e444d7652dcc44d8984ad500cf1d29a2242bd30bfb5bd3eb76e7cf673275c21f9ecd128d346760169d28ab130b042b4111792bb700688ed4c313c5f168776bebd4efc7b5c8a244d8ba035bc42d039278015f319ef204e7869582a42a617cc51566356ba50ed425e8a4b749d81a0ad6f18abb1f53267cff6f65e6714c4a397efb80184e037c6327175f09bcaf7e28e8c9aee1ff608389deb8627134d0ce9fa5feb55c170e782d5214ccd605eec1257df978c84c72adec12e2b0f3126371d1ee637532aa455e2d2a78d62afdd7180045f5626941bdf1f7849adbcb24000d1c9eac6d91adadf387706d72b5dd34e804d4b52193f0dcd0d219a9063743deb5f4349336dbe6b49dbf416c1789d3d07d304e7354d6a54ead2ec2ba06b67b345473b6e1a9b1c26156c39facaf76f899e428ccb16ec1924cb31fb117f22bd7b131cbd6ea732abfc50459469e76d07dd90f1387549c3b417078677264bf1f284a099584aac71c07e7fb7363c60c868f435d20a32b528b59ce742f5afae859176c4931b55e6c0fd7e3b34fac1e1fc4116fb61da0d8cf4cb32d83588556adfc358e1130ce98576c3a350612bb36da466e5e430cd2ee1202e4ba044375f6bb5082a688a43cbc32fa463585c9f90a8572910d9a06c16854bbd5268dc754d94d49f89542e8cc8b2ef418ceb21cac5bd74938c7fa1da3952a91f4563cfccc6eb1946a3ddf30c389085a4ab95a232b732e139e86200d1a9770453f3cf095f35f9ddaea29d99de184b8123a5f2b63c0e824c16207991ad29daaa0ee898629c92a646495916aa6fd424d5015b85432cee703ad1b2aeb038313c5e1410d79237e6ca180f40c19d5e5b2513a1377ca2fac6715217ba9351fc5b3942d314b2e93c71a2f96d9155db47b6d78e1cc2bd004aa22003e163b6412a752ca2e7889cefa2751358fd0fbe3884d99f106d68c973c1dce717a0f33eb02837031a40957b5fa4835b44747471801e52586ac30295db9fc1bb1a704f18b0d1447924bf9f1e02784cc1b0532824a4c078107acd6fd9db620fd1a61a0b0ee0aaa2950bab91529b7454b666079592dc6e88fae0ed90f90d35892225d47a4862f8963dea3b555a8f01f4da08bd46ae1ae6bc16bcf7005347c8c19de6c2145ad125aa502df4cd04faff782bc862e046cc3240bf2c8bcd04f70bb4ef6d36fa27c1677f6bd1cd6b98983ddf5012b2f031398fbf6ca5c8a32da32cde2e62c75369011f86008c2d4c0ca000b7ad4499d797d6b91fa77825fb44045e75a9216249177301422c032ca94ed2749e37ac341bfcd3d25fc7ecd745e66051a06188f3f4a0b07383c49c3ef429d01566aa613cf191134cf3431342729cfbaa7732cf5482fd72aa56ab908372599856c78c0b74632d5d60966806bd67d0f82f96c58df6256021c260bcdf988a10981f18558f30407b89644a7ea76d78c0ddb151d55530b667c1343edabed74cd778cefe84306c1eab5bfebc5d97b7791300950279dc2aeae2f235b102f73827cdd964704d11d5240f22d7da11fa6afa07b841291a2c1a7ac492c757990536725bda41c4083e8acb511abb6eddeecf6e50539c4c011b3d1d10a27f1b8b43e24e1a85a932f807aae6be08145cb67c932c8a20452d5cdec07bb06169254ee048b3edeadb4c9934efcf8527debbf850f8903614da37e81f52853002fd4cfb00556968925d6b81c10197dae197b9ccfece01494971e15967e157e662a022c3517eba2f66934d09cf0610075120f622acdcec3064a52150f6d095a2724e0d64d5824673d815ea1dec20dbb5b193705c78f2b4415be9e5c2ce67e2d1d0846cb6a25cf5de0808ee27abc17f5508b07f104a3f3f0522b6b226f4b7b8b8e0e999218e897f2a6d36fb1f2dbb1e4b57ce685332a56be535c6c6cc5d257e9ff05a455f3cd0d1f8ea76967807a251b0bcb3a1cd8da51868169fc2f727c528545242850473b17de1e572da6cc3796b98847e836d80bf2ec2618287d37d544866004e27ac002f00d5ff8399f2f6c541c3afde128584ec229ff1bfd92b8fb2fedc826a12118800da2ff24c92d5f7d15f409cc23b8cccef62cade5ec8af6270aaac10e4c6b05214f6fb1f74325f1ea5d67238b2789d21a634c24e687a7636b7ce8590d2c3adb23cd6404145afddbf5e18654b398ac0a0ee354b6cfd254d9971412c302b57afb73e644896ec74bdaad3a21c268c48b37a0cf7970a22e14a95f72addf13af72ee85e567fb98daa6d94ba88d6c8af8e02bfc037fe0624c438793677591146cd48b3da4d2330c7cc82be58818b5c96ea094be4a314c8c1c3023340d48f417fdb66ff71bf86748e95ea77dfdcd27d5593241e172d745dd8ca6907f47e6b207103a1fc9bff0ba9cc5556127307bab4dd8a9cd71f62b579ae35854b736af3d2e7648230877b9611da0c0e657ffd48e0e7982e1489b6a9a37943e6cb947bd2e0abacc109c9e01f390f28eb2bc2264e2e865c46662734ab8a718d3d812c8fdfc04357462790a6d2f5ad09b11f9c4954bb8a61e7f7176ffae8ddb6733acd31fad0b5087afb2695f9eaea944cfba9505200d4fe8788a724e4aa60b165b54c35cfc2ea275976325c84249e203d793ace9ad8001a255ebfae02f564a79ad3ef9946c2dde6205a2c3b0fa8cd8e0b431bd3b0ff968a9a97cb61f2737fb6ef216ac4afb121d720587325db079dc26956af3712406d0ce60be27365339434aa7b9ee129001b0515f27e5434b9ecf0e445ee0a45d3ccd7774a54ce036ffb5177730971fc814a28953fad3b536c531909042ffa1677de8fafac8eb7db383dd9a88ea4527f70de0966b2d3bac048cc489c0c0c691913e79bc4f3ba6ac385d012978628ad122098600e73b68eddfffbafb4022683ceb1c5d689808d8f5a5fdc8ffc4730eda3426a7caf15d866e5284890ce098709e86666e178cfd0689f4d1a747666fb7e7ac084cd90565505ddf40fc09aa3e8bfe26c2db1432e60f0eb404aeea8e4249a6960a303c3fe143cb55e45564fe5b0913bf8f6c982c4c1f7649985707ee9fdbda1712afd04b600ab380d70905cee858094ca77f0b8a595709a670dd57b8faef6455179804b0479438dceab774cb4d960609046dfc54837771e26f656d613a1fb1831c84c7318ef1b588cdf7d909e123c20359af82e3d0a9bb9d1d26554b009804462a6934cef992bf0765f824d9986f3aeebed385f8731ead30635a6ce040c50618089e2e038bc669e32c3edcd131748319cae641a697f5566484a5b5504973d80b1b3f4cc0d1cf90e917b6ad033ec67e2604689987b38edff98d9ce4bf410f07f320b79c6e4192eec6ea444dc7a96af93823e5f114a0a21b343b8b6ff1a5f767ef1e0b9c2c1e64d85aa608eccc55e23d1c485f9a020aef106f2d718294e71f74ce1e49aab051f13f1142f9082466304d55db810f59e935482b661e03bf7d4200a9033ccb47c4871c61b9a42198ac6093d8d541d270d61e03a20ecad6e817cfc0ef30636283d0dcf39193aefe762da76c6e097f0a739c646ae0aea623666a3c85850c4f1220292cd457719f4071a133247a33eca153bb23a86742cd45e9f662cfc70b8dd504c56ce14ac9281add81dc778f47d84a27cc8d9560089a06c0190f50ef0bb97e18ce7eee5a4b05824fc996d8be0be1a8607a2dc26b9081af9f4c7cdc080ad4f1362d634c656a60a00a633af090d6e88ff387f306dd112205a300be4b5e175a0f41ee2865da3d79bd45526801e5032bd937f0f233f1ccebce99a43140ef234c7aefdd9c4a8f2644a9737d9ffcc545c288e8532d239c5176e230d3b93bf805c0ef1dcfffa75d1d2239524bd3a31b750b503a1e80c93d53457ab6aead7905297ec80f9ba9766b86db4d469ebb8e722662bcb84f4fb56e4030d482e9eff9d425dbade57b6986f552847e2575ae031911abf1a32e564a596ed576d3485de6401df28c94d9f4881155e58c6619602c2a468e27ba9b3df8c12199bfb949cbba410075704eba55021d5b95af43bb0cf783f84504a364fb940de65bff7eb7af405023a786e30bfd564da19f1155cc39b2d3f2a6af67b3b5204687917587c932b06a1deb041c5f0aab75a685874b5e6562c25c68e73155641b3e66e5567a553a04ef0ce570707daaa4b832abde636ff8a551512fd4c098fae9788026a28929ae4f3d09fc6fc052215b7b72f281ad73b3d4ba061286a713532fc71c54d264dd88c40a139598384a5b5090ef33fe0136e29eaa89390f81f3f46c2e61bee9b3fe89ad0025fbd0659972ec4213a2c76d87aadae18718d14abae030c5604ca57dcba8743b00bc5ef116254de78a4bbd1cbe1c0a07065296e671bcb8799caf900f24ea440f1ace537f57b619f0e5af6ab81ab1f229b22e47a9f9c6f8ddb4af9672e142cc4a172c915ae763d93adc9d47aacf9ecb9564e662c30741ec9b5c53ba3683a99ad800cb93318c412ecd8cc68042f1b2bbf47a3057d98ccffa1119780ed2975d2b031a9e72eb5031d48e4f38ab89ec302f2680e5bf6cdecdb20d9b9b3045f14c414a04ce171fd95093b88239b7b7d2705e385c0f598c8d188fb51f46f9f7405c358a2721a2fe172c8f37a88d02c0480694867d40e734f04883503f7acfff6542084c17e4a9834dbbb1f0a1d06ebb8edcb614e50f4d9d69762f94874d04d6fde4c0752d806a1a856674ce00b07a6f0db2f0406821ffe52ee7bb9eef56faf3b678a70b0056caf14c48cef88874a5d02c859c85256e1015202caec5998878c9acc0551d06eb603def682de60f7f0a6b42ee075b80ba4fae308126cd782d7907534fe71912dfd5fff73789282bdabdc9299d7baf436ca341104bc53cf83e797aa7f785101e9d723ebc25c4cff66b835fbe4041916e7465104f9b98478565eb0bfc2997fc0567e4fd5c256c85f68fac3ee24e9319f14d2405076e2a72bc71fc5f068842a71dceac206bd01bc8e72cb790af500bf02e65223546cf33ca170765f805f944070a46b061b5d11b40ace050d8cc5feb16f7c4c9b438eac11ae5981a0618f1d45c12caba43cc1af67eee753b03836dd02e4086eeebc7383e62ff80393178cddfa30efd36409f3cce15a1f3ac25691bdcb29bee5f05aadae6ad7338c675b2c4f96506539f855e4208731d60c46aeb34602b935a8f721310d9bebb0d1bc55a48947829e0aff2871271b07d65be03044bccd3983cc2991e66e1d4fb79f4a45307913326330c5496174b12a1904fc8504adbf956d3d00caf4d042a1c23d426ddfe81de04110ed4e9d82d413131da701c94510acc7894d2c5783821fb1b2947cf9b9fc815c703538175f3c0cf2faad70dda3af3dc6967bc4162e5dd3f398477700f110e24187e1ba95407833ed314bb24f38410df33bcab59155fb173c172220aedb46105fbbe750bfe20184c07aaf6ab485047c7d2fcf738e5519feac385e7d74e472e22b580993c10199c9a264fb71fd89e7cd56eefbbda1cdd63962d7d30107481532c63d7f82930de9f0e3e2c7affb5392975b7d7744702e68b1766ae7de38f60352ca38e67916736b2da3e6433726cd27b7f17e76769b44a03fe3c4e2cad5379c529ab3694223c72b67c02c60057aff7003b3e53d1ec59364a6fc561c7b4b184db2bdd79fdc9704edf0916ff4740514c1987e2f2168cc54b9dd340d02e259f99ce01ad9d5d6944dd52a978e6ba9bca1d277dd21ef3f6928cc2297ee6e76ad3256a15f1009a8956ccd68a338c2e099e5afe9b3365e5818198cb107a9f70be8add7a0ba76731923e8dc81ec699a0ef0fee7451bdf6fdd54f16e3c37bbeb1b23ab07d11a887b6a1318ce8ca89bdd326a988dd00d3b4ae59c1bbbe540469b4b3794666060925b7acc6dab414837c03d8ee123e09f87cf8a12f05c85f3ad1821edf8d12187339266d5d2a9bfa63eea5604415e1d7dfcb7cf9a3df030cdd014c5c615e340cbca315a89dd69f61b352c60c2ac8329eb51b4cacf86f3c467c0199eb1199f7093628fe7f809ceb200f8b262781c164f9e96ddb9ad897fcd1822bee0bc8df832c92537cf2cdfbc0e4d83553deb8249c5eb8bc198f1ad13f00f38e855883a4812c8692f4d277a5639d047410ad23a154c3f18b76e4b062a2f82eaa89035615db2c1f23e3ec233ad71196965522f67120d986e10b52475585d7398575cbc8a81626024b8181e049e4889d5939fea09166d9fe502e50f8e75b8a39cfeee63386890495f31496f1b60165d7afba12d993e5f3dfd22b89a1af0fb254d1151f1b263b2cc367575e069b0d0833cedd493be67d216fdec70fd7e4ea0311ffd0c622838c02297e7a5899d85528b3b21b8128abb3ee080f203fa7ef135170f4eaffc9e1fd0ba8712161528fee2c370ac0f78a6325a1f64768309a307def9f9192feabee851b6ec658ab933729028cc58961923dde03438a74bdd19ae209d65dd2a2d1b2f801c07e1b41644974aa3a9de8bb0a0054e1e4e494c2330029e9121930c07e1bf8045674837a5913cf33bd338e0903b9fb9c99a63584d83fdf0ed16c4174334f4a18138d91a0b174724252869878ed251e5dece675925eec92cdcb8215e5e4962d21989b001f106ea3d1a5143133cace9939d4bfd6b263bb3d9ff7853042f7766118940257dcbb06910a1c5809866a3b2d5d12fcce6ca65870c1f9173ff5c9603622ff578cdeae9d93d70ef9f3be40e103ba76d08fa6093dafe5c1b5590a63b9e6304326fae968c3e34f8880064566f2a96ffbe50721f2b5da52d43a5188cd710b1aef98bc4b71623fbf42b824b15a39044f7c1875e2be037b0918772b3a4b433e22a71b722d0878620af6721d59a1053c50234f4f03ca953a37a156fb6170f0190ef5bcc4e73421f17752d6857e24e8a6adb26f80c34d96bb8e613ce3918d4ddf2ab74e92651bf9c4c7be3eb2ab58693be33a8c3bfa0d51d077d9e2feb526241c058d33d0d04a34025d6513192d57dda67ec1800bca63872ed03c866eb1942db6256ab2fc551659b6a6ac38c0d3a218537151ac2497e6ac5d1a6dce982811fd2c2f72c2f6ecbc03420498f9d2d19c28416b931d31eca2a50bd70286778b5d5b3e07a9fcd192fc5c1ef2b71ae66a29d9b86c423a24c3f3d6fba8e8b6fd3a3ef2e621be45cbe7c8a823068cb6169ed79d25885594c68bb5707a95543a8dbc98c83d2593ebf226e6fa831bff91f121397319f586173e4fccfc25eace77d338669586066c1d07f923cda6df3e64acad98fa0c9bd48f3348d5891d153d215f0ef0935e04b3d439f7ae1db0ba9c09160acf0c2dd57dae351e81a229f10550f78569c69d10e0555e194d2feac7c390651f902cdc61a031e3cec9d194ae5a1abd14ad9fb3155a16e4c965278c08c26a4c65b9a7999685e602dae2f686e6eedae39af4cf3c1991c34e32bf7e092b14238f99eced1da6cbb658e72f399c5e2204cc4517284c2f2940346864a12b66ed991e5d9e54daa380c27ff63265545bf809f1cf6040c62d1dab6c55cbfbeaf1cba651c23b6e4183365896bb6a75426fecb5d7e8588625249f65de21a1fca4895de7b13d96debad13bd97edfce751359d75e90dd0ec69b1086a85f55e71d8c65942a39cd57b3ddfc66d17720f40935dfa4041ac91b02ee2e0b88602eb808552a94e2596819a2bd322aeaf970f0b9f25520c979b5ce334e1e4c41062005db6efa42f26b3afe995f3c612a3207a60cf0f86d9afb2f5c3c9e2b98ddb4cdeb681d7a79a2a9e39ee354e55db4f9489931d684aa43609c8c51b02232d661cde0880715c6d7b0d9bf48da01e99f43d720ee4b6d06cf67d1ea57472ad7287de202f92536196e5e4e7efdc289f2e8d29dae80c0ddd34e43b06b700010c6d2dd5eb40b30f5c1f34b1cbf5dd29a9c6413b404cf7f4d5e44c6efd06ebd2f61b00c6a9a23c2ee4a912a346bdeb353a24bb54066c2b48f603ce10bace6fb2b6b2f67b01b1cdf7418a0aedea179e0420b190977ad849bbf763ea19ae22a641e5416a795908c6a5bbf2fc3376ce7c9a189e4852d0a59b1b31cf12bbc26a43c0540859ec14f887cd35eade20a86905e21608136a7b9225a5aa16a9a55318071120ea66796e1e296f0eb89c9dd8a77ec55ba003d439189622adea8684f9c0d7806f7f6a167a329588ef99908d323e4038d7888fb2fd558cca93ff8a8e360148e0c47df63964e87a2f9e5dc456e9371cf436f04ad4ba531b2560a9160fcf8ffe21c8a6392a254c34a14b1fa07415cb779b4a97bfebf9c856716c497c04ee020dc2ba8db6e64064f6a12864b950b4fb8943d07c192c6b3c64636d04e920242f0b821e1e5e9468a4099bb85d0dd20a2ca0ade28850e0c75a29e17d7ea104b967dbf1a0fa7c3ca7ee2fab640e3993c5034cc9db10e99422ec8393af23fc1244823540c04d3650598e70a47652d82822dbeb095bb5422a85ebe8432d25a4b6c4bf1ff0f6522a9ab211ff1043c79af9f816db1a3ca129f5ef5b11c482f7db11867ed3405f151fedc4ecfc5d689a334802fb029a00bd9764908c84397b8ddc832d2a6ec0489befbd5fed24727e2449e82ac9a1405d9a21cd2f57451b548030bd8b00cb41b999bc3ddb385c86dee4bf26a1c90865a9cc3fb683378cb26248fa87424483120ae64deebee903fba7708211f871846015acd4d576f03d8b04662244b5a64260fded3e599ab32a66c2ed158174d3c791cf146feef2486d3458fdeb49f2d2acb27580f9291d1eff894607e2883f2efd696c8d34ac9bfd4a586b8fe056fa28f6e10aa24e33810e8b61312f869c995d4aac65dde2d094795668d54b0dc219fbc062128339cfb1a949f000a02d46388c56437439dbf3d755e85880f52510c59882120bc9892c6ca86d070eea1abe8c2566f701490a9c58012eaf3b6d6d126927348280e5413075437951f5f113bef1d4df52f41f45df426552b611545d37697ff1f0292dafb57a127f033d599a0e08071988f73608d79181ea1a4bbae27414bcdf315cebeeca215eb368de29fcfb40816567217203a1d1597dfdcd00b5220a802430a9ad88662352c218a6dcb7b7e081ec4fbd5bbd0bc983b48d405bd40db07970719874b5f0f23d5cc8242a11f54eb44441ad3afe5b59faf20a86bdc466d2af63113b7f9ca89a8532daef2d3d094372129a5beb0fa710ad73f7aff437585bfa2f2153d127117468e302cf735c858a1d9249dd51377d0c49e5c5087cf8c8e859e80187fd2efb29c999cca326d9faa50bee31813c4610afe50f2e78e140fdc05227c02223b0524a911ad80988b7784337087a40dd271d329432802b6eac492e7e57a0193fac4b5b1b03a48592cd4211acfdfa0916177c1ad875e3b7d60bea7f023b72dc0b82e002721ffc03a4e8aaa8bca823e3323373116bc9d3577f80eed7df61521e0ad3fb1dfab09b965cfa000dc910eee2e99a86ec7debedd1bfa6eba05844a1a880f9155c742f51e7b96419d512e48f56ba3c42b5d7dec49c3500b16c2084c025f86494d8ba7df6789e6ac2df2131f4ba25d5d554d75b2cdf1eeee4e44d6e421d8f4dfe37685b8da071a2112f4d690cff5e495994ac9c2af79f6d1244f984427f1e6c51dfb2011f9d1154ba3d84f2b23b52897c7f2f4a10820195af3d0be462564326ea6a7a6d7e9b4131408df2c8207e26410fbda6e2de4f8977c53fc0290", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da04570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a7110ecfce55cd472966bf92d95eaa2ca0aa89a06129ca6ea67d7b5c9e26e4925e01de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811088db0dd523e2dda90a02310bc443e2796689bb889f1a91f22c7e910915558a7279cc5790a050ab4e07fa5176b9018996360291d94e5cda28e417eb2fdd3e8df" + "proof_hex": "0x00000000000000000000000000000000000000000000000f53dbb3615cad8357000000000000000000000000000000000000000000000009a1d34b7e2d8c2c2300000000000000000000000000000000000000000000000e3c15f1d9a07c7acf0000000000000000000000000000000000000000000000000001f8f7472abfc300000000000000000000000000000000000000000000000aa2049ed9ff1229bf0000000000000000000000000000000000000000000000063cbacd299c0eb9a600000000000000000000000000000000000000000000000370dd16b6afb381350000000000000000000000000000000000000000000000000000a4e5b3a4081e00000000000000000000000000000000000000000000000a21338638546b6e87000000000000000000000000000000000000000000000005f3cc20e5b7b11c6400000000000000000000000000000000000000000000000bc860a8f566b2782b000000000000000000000000000000000000000000000000000185152431351d00000000000000000000000000000000000000000000000c51f8b01dabc8adeb000000000000000000000000000000000000000000000007e4929e969d92cec900000000000000000000000000000000000000000000000ab999373bb5814f010000000000000000000000000000000000000000000000000001b40ef30bf3c90451c5bb2caf164d374b7c96c358e5baef15971e99b5260f38630791d74696bb23220fb9443bdef2a7491c300618a5f75506aa6391df2fc8ed4e2d347c1ad4b90f9748a44b88ec4f79b09d7406485355f3c44403899f531a0b2efe309986fdee0f0129a31d8d123d6e44f569ed814f142cfa5309256218f5ade73d5caa0038961ac66ae3b121f738a38109c13e385c269b0387bfa24b993409c01470e1eb63752f4d9cf747b975097a06c7ab48f11ccca1b0848cc6d75a60756bbecda66390e81588fea840ebaada47351091f07fd53a0661cd6c76b98cdd061ff46270959537099a6272f30de4490d132fe4edadc9f7f63c865c1265faf7c531764653cc60c307af7f4ef05bc70dc756c806372167779f52e94725f5d5125b87a230aac388822cb72616e045a2f20ef10a251550dc7eace0ea8cc0604c9c4a624773daafb28c22548630273a92e339c80321122881fcedce0be138ca2a2453b4d94e27468b0622cb8b550badbffd877a3b93870669caa8eac7d39a5d35187b212e46249551ee1730e804feaa9e847299184ded492f4e39e5e06f736c084b9597a5877eeef8a905eed894db2b6a935fcfd8bee52c90e5c405683cf4d9f9c982ea99ff6b67fd882afefc31fb2e9ee8d8c69e5f09721cb766c50c5f423e817ad74c752aa61d353e19835a0ba7bd6056d2747808cc8e621c3df08fd2a3cc17ed5182452b2f3594f11bf0011a890c5bbbd1052bdce9c18651d153b1844e11a223775f54554057f4c716e3b3bac81dd1c1774fc0771916646d934753eb63f6d65a8c9d9c304ece2864250efaef1edb76fce82029ca710eee55d492f0c6f789ed0b07d1f0b810f0bbde0c26d8be4e8584b10ef4a171cc09a0f3269668ddd8a740cabe08d77b31d8e4a70d0f388962112b68f542184b139ed278a41b4c7d7ab998cc9b82d2465d0e315430286d6a15ef18c7581365648f24f3fa4ae77f021e320939e13a5899bd785404112a639d3332f81c037b404324e6e1c93c8976d49ec69eb406e4ee7dce59ff8908aa416ce08e224e059c1a6380d4ffc075e74c6c45281fda49db67a94cef19501270dedc9e1a490355b61eadc03d55ae1a9e2cae2f990e7fe5c3c6ef5669a29007b7b65d0fb59aed9e71613738a1be1f4f5a3ad7858307e95730592734ebda4d2ea73936c515af04d5e6a4316c20c86c6203091399a3005b3c40f57894e0d5d80f0cb4b2858117cc94383c6bccd0be969b5e517f8a3631702145dad752591c590b61a8e3f833a80a0dc69a0631714977ef66d1d29bdd09368477939fa03b18f51e49b2a522478dd3b414ccc898864a55768449fe95cf07d08014ea2c5d236c3d0d423a0a9b724ac5d5644104b265dfb9cb078bcb22ccf44b471efa2071b5efad14c3c88e764ec75ae5ecd4755b8a20d6eb6a25e6e9d37f5f76b7fd0d4c6654c51344a8912711a3577ed6d8aaa654522776d1f12e4dca25657a2235377e65c87b2f53fe531de60ab8c4e3592a0d36586308e1437041cd07327dce0fcef14f050524d3fde7e53cfc434af9a8c646c8e9e231bd8286c3803c89cea564a9cd0f022b2367c7f6afaaea30b89d8b44dacca4e6e98da34fe8d97ca7315fc5bb704282d81d99e608994e0003d7d21b0e60e4db83bc1272630c2e8fe23a8a64ec6fddae7c1d72f7667c3314bdd208a791900d672ff419770912924486834068d7205a229f1f250f56bbc23667154e10c1fecdd4bdb0708546a5b62ccdf1195dd0acee346c1fc460a412d44e0835c25cded8b5643d0585483df4b03afee6bf2b0be8167bbe11aa158a875999a3aa2ef9ed382a30be7bb9566c1c4bca9961483478fba6f0d92540de51ea5f67a18d7a07625754e8a545662bc73268373d69c61dfaf2dc53b508970e01fd5b346c57f10920822131d075216015a7f3e89d66bc71cf5dac8e4b00b83c12602f99d788d626d330061426184da2ed4056bce28e306949e76fcee71a4301b32d1f67ed77452939da2e285b46e13266582823bab2f1c3035f2904330df195c043046cf89571ece49a6cabbf9e943933fc1647058e09d05635230e53107787d4608fff7804d0ff02f3de3a66ab22d381c7dfb51478d84887fb55378e1beca8a3d4cbfcda3b098fb66d7ce4483390e722e9a936e542e557ca29a54b2e281aefbe586ebfb38fe5f68d8ca178a51902b63f9128665dc84bb3e86cb5788722ce35a7aba3ae887525cd32363849be1430db7013532eb4400ed55d0e71c8a01002152e48987a0a5a35653a6ba485afa079caa2a520ed9f72586ae8d130f9081da06b7e167b359623d5f1b12e6a9bf18a5f4d1aca33e9bd3e4fb266c360d97b002e5c13ae200ad7ca60e9677f73eb79a98b2306ebbb433f20f148ceff4e06610174c9bd1dc7b0030d4ea474c642f9fdd9a909d133a65dc672e4f069ff2da59215566da4fa08a76e3e2a9634fe5790e0500dff88ab6c1a3f047070202223e36f05019b3869f887544fc400bb3b51388c62a96103bde7b67934c0ed0db9bc3ee72716bb9f0271550deb848bc74a28a1aee544acdab6e3472ac25eb679493074e7014987fc4f8b92c0220ef132070a3925fb0f734727388762022ca679fd9121cf23a27601d38d23c2e91a1a9e5d4860f4c93579b0982503e58792dc9ace29455126474df651589a91bdc5b80ea645e7c44f6c3c05403e34ced369acf3a8ce7d4f1f576103a6d13f8b15b3b2ec4382ef7d0370ef3c5db541d4d54c1ebd5632fdb4142b3f697ac0e94cb884cd5e45453e55b52d552c9330055e71eaf6fd613ad8611f7cf75022c65538928a20f760aef97960fa36243b5db8a91c61cf26def5da31137ece1dacf10661ac08a12d484b143e6f45752d14d7acc904bc5a094a6fb3081a05d242fb83d6f84e91e965381bbc9b11bd55a0ea8ea6fab6c8e289a0f147bd1e729ae0e3e422496cf220b55b394cd4a92fbe43c5034d44e7473f3559371de1050d4e1b0cbd01023d18f5087a619a78cf0416cd6e2e5dde211cdcb0e834c9b9059fba6838662bed24a4e9f37a7583a2a72de506e384e5c70c465a1fcd4cc42800548b9bada85fc3596fbfff9a508fcd42bc0ac8fc3972e86bb8f2376f9495ff29b1d10f89705da570c744b77cd3d7321a122d304ab4ffa6d2c6ed9c760c4b6d0fe0ae339a75734f57cfca627ebea65eb03edbce506a3f1722fa8d238b4db0371a6327eee892cc246d4974cf9b78cec65bc38f4835c1f5c4aef02b2b54e71ee405cf45d31a3432cd7afc100dfd50ecc0894b19600ded8ac6e02756f725d0f19914f7478e44afa64e7d00fe992f1d4f59872fa1b8ef0115e69b1044429c4729c117102d5b3fac786444a171d7f6c80ac2f9f5ec568bcab3c632921dddb5fda2410dcabf653166ee9224d41aa0eb9ccda051f2c800d792803088eb8a707b3112342b4a5a5ad021bdcb2408a88cf4277954944f174e04aee4562dd8a18ae6c119e5253df10afd23922247552cc74a55c13fb5d73249504a98aae929d2e3aa3320991746baf937f20dcc086281a71feea77f316d06bb62ff13ddd03d70c82c298c212d7365134d76b3a36d9325f834dfbb84dc4978dadbbee821fd814eb842451f9507f7de25ecd150b54acb7ee749e7b6cd2014c41bd6fa31c468376955aa6ce1771f21a381c32ed7c504b42b7ad5e181d8cb1cd3f41b09398460730076bdcb33ad093868935a0ea6953389948cc9fb8526dba609fbf4d184697c29b4acfd01c6ca2ede99c6628f0d99ea7ddcc2ee8c3d3284fe22d4cf23c2e92a171a5d5cb4188026e15e0adc191ed2b0ce1e0a5602e881eb6c1c0f65c125e20940f59ae5836fad1e44a41ec4abc6573d7bd0002816c5fe88a49e05cf81803abf305229f613d645007c8a867254edd13ee7afc09373639b0096166e232a9093ef8f0f96f8fff7950c81f2b547b48db6bd2d98d66d125c78a5981c959fa0a7bbc845204cf596506a2b1de12f3aa527da68e14933275f799b0b50c6f75bd4822c429067d92ad2cdd61fbf4cc7fd4e75259d33ee7b31aebceccfe19a0d56797a53d8918a8861680bd704fb6ea78cd2eed94ac1f7ad34746bf4e62867fa8e77dd6ffb5ad2b0ae1f61071ab9e852507202a97ba7c8944acbc01d1d352a8f1dbcff719df3bb7dfa6636ab1295a26fd3ffc8044b710870234c8ef04f5dc0d709fce3554d1043b3ecde5c7c1c177c30f249e44f54cba171e7957e1c3c039a14586873a8f84e1eac7bc6e87c20cb54fd0be3f9b64310707b053917cc4274a90ef0ae504b64f6589ba9bea46525bbcb27ca392e2123f88fc5ba0ac269a0caacbc290529edc2d9e4c01417c27c06dcb31575584c7d839d9411e50c81158faaf2fac899cd4a4ef948b99c64ef36113e2ec7327372d2c48d6fe310ceb9f4815ff144fdd2baa33c8df8157f2c8f17229e9ff8cf302b36ce3a9f5da4ec3f2f9ec74f76fe55765680d70e24485c202803117d860bd785da849d43a0db09f7577ba095bd4c02519ee6941face8d616432010ddc416bf55cdbd4a6e9e6efd993b0d122ef34daa15f86e93dde4577e287811fcc7c2db977428feadd96537ba4010e521ce4e4ba75c0943d270cecbbc045b1d6e9db72bc59d25dda583d413f886976efae9075987e1f5078933f5b0a619e21c855a147257d70b7f9657cfabe2681b1864abccbd9d69653a6b4342db67af2315ac265b2d627b21de5f070e0f9737313e7792cc7ecb8fc9fa35b98a1f8b56541351d9bdfd22101887c6debd219a39c786fab7c13233e3c8c742e62f792377e91e2fa5ac896c489867c3dfd4a954619964d5a4dd306e7e5242974c29c7ea34ce0514b1c7a04665cafe79acc47db2325fe52d1b978ac531afb8439b9b5ee8acea11d8630ae5a7c486f1df468ceca9ca87f30df23cf6d3565e17f9fda6a1b1a06829cfa4d470d4433e2e6d298d99e6c49aec3ad1fffcfee1966d67e886d14365811109d205318b09a7c2309cb7d099c2dd0e4326c5812d5c966bf5209cdebff0a01d79ad7bf8847b41973f0058dcdfa4a0ea8105b254c04e1fedfbf588601745b52cfb9f6efaecfc1fc5f07816980a450a1eeeb34244e6319ddd631497dffe58360b59309eb15f63abb0d0e6c099407a0655e816f269c2c8b79dc3c87b7e18219d1ec346fdfbfcdccb0a403e7b176fd0609c36a3f46ce3df07a873f475ca97dd35302610de77bc0973b3c2370c3464753b722aa5fb9d760276dffab2a757c355602e3e018f31470db36ce0351a1cc0823f6321c70a0b1197f56e54d0e9decb0fee29da67b8ec7c485c4e35ef5edaeca1f8c5ff12ca57f904f737de97fcb72b81122566ed4226ffb8ae70b5fd984784564e2063b0a67c7fc04f487f8b73a3236aa31afe58c1718afad7a68d28d680ac1feb069d008f5e13a4e338ead29a7ec333632cd3cb379167d23bdc6147b2aa9fd8d3025867be3dc8af5ce3ad576421bad8fa091591f988e3cde4f684894e57d1d8c5a08ae11bfbd79d03734abe1e40cc1d1a236c94d0b200de14087d54ea627a4aff7f21ec30bf0886f4ddf0d404f9cd322c11c4bc3db97075b7a7fd10aac219b19cd12d5dc1eef506fd93ae9fc4494ccd2f160b162901ff434af8224f789e42307741f394447d2d24e1c61eff6b26b2238c29164909260ed975641bd83a4a5a5e5a3a0d1438a63fa284aefaf826bc11d12c10ee9eea904a50f33047e55a82b435613c5d7f5a63a2c202e8b6a5a161f2930912d3b9bcda4b856a658640ba988fa7f64c4c242e3d2cbc44ce892567be98aac917b423ba0b9b6eeb97ab9db16a9b2a2558f4a6dbaa63d847387114020b7ac22104463ceedeac9131a14448e0079fefdd8d3fa1064750308bb21b800b3b9b88060d54d8872bb9e6656f261269605504b6828ad43565e5c92e0f451b3d67b5b0f40087306a467a3f9911f01c5fff23391874cd52c9edf3d95999b9ceca945ad90718b3d476d9a979d504ab1d3eee92b16964bac2833756b9ad845a6cb8df462b2525b10643b72b7a17d9b2e7c082c2e368af41e8bde57b4266f3d4a0e632eea2ad0e66242db844dda07c5fa18d4c9403263fe34f033d057c9771282126966d312b26ff47309369bc3bd2e0b8d327bac8e1b7c3e9cd2d64e723d33869416b4a1ea80b180740c06bd7521f8c77f5798affb5a1d4e6aa2f2b99834c08e78f9d1d9a820a951876e47f6cd9506a43f3c9bd27fc4422dee8ace5747bc95551c4e132f43d0e44a1fd8f56263a6316dbea89f7b3831f4840ba89db2e72fa695c81f8fadeb32593758659a927e047060ea750ead71a3b1b4321336299bbe6702f94970f6aa817f6c0ebfeee024701c9453c5521563fc9185b39e503bdf7e7a0542d8f7c60012e0ac769cc449cc2b913696ee7d4b98cea6ed65497003679bc6651807189276e28c5ee15204c9a7af665acd087092c030f76f5776a692dd730ef82f857eadc2f0e6b83cf868207ab7b2a2980c3e4d093913f57220d74b735958fddc92adc2f2e157b5e15f3f87cfb67fe1d2e0f684538e1aaaeb3b654b260dd11ad5838dcee0723f8cf1a2f629f62951d7be72b24f937d6e608387d1ca69c741c8fbb8acc3c97073c33c17d50c487e25558c86e0bc8b496c4c30269f4766480a2cdc28aea39ff228e20512c24f9c109661e936b3d42093c81d5ef0227ba6271af8bdad0412842034f6c6fd4cc7f63a79afeef71b40b9e26a6e1f6da2618e1e0335e6aa0ff84001987cb3e414a95a4813270bab48f897f38feea011b2168f0fab126fb081d24320d548a17cbf26696f9402b88592bfaf81502d38c0a595c3fa3f2069545729e642d2f98492854db814e58b533f6db719bc6163aed2524ffdd42c3a48166c5ba7f21a580c689d59db27b30e722fb1f118963c97c14b40ad4d7fa03053eed87ec670061f2f4824d8bed8a4fe6401d35372d3b42bbdb6ff13e546577d8b38645518e1ec0ce4a9d670b6d4bcd01635c8328a89cd19ae391b3cf2ddf692a06927c378c149d8de4d7f0199aeca659442de514851b4f98af06c897b6ac235f7d0a754a3015b728c70c1c7064c98e1e9b97c617b0b2a4d6ef6a70357825764ade1fd9460714dcaf36c3db239ec639ed154431c80bf4df9d6c6e3065d966a8c8240fdf2209230dfcd13621b17acfe853445db711fe9c7727eccacce0bd66a732d0f2330bc829320961f7e64285f0c83e62236c14f4c43ae25be3100e7a68ea5d2c06aacd752b5c965142b312acaa3b5127c5fc3e4f67d273d2411542082ff83dfb6415306b21367cef5c4ebed8e8ff742691a461ed9398bba83e65ca8d09189dff137664f22967b1445325643c0167c81f10f27226b608ab54d45f8ea5b2680b0ba2ea657d26016ccb38e502f41cb4b29de0540dd0e8e77c082fbcb41ca1af63a74fe438eb0349344d937ca9b1c0aa5e230f5dce035511c9a78d603f7eb7a1988bce41beae006995d37a978272226ee69dbbeb858f7c7be00c1679f9ce69b598635a14b87b28d8c8024e7a8b502232719c781dce4ac0b2faadce0f653bf0282dafec92fd6c0ee7b3aeb390f19b6e9a674c2bca397b89d00b5f2380da7665e3d27bd0f0319b28416709dc0077be3e7c3a13ed36f0c011770f660532922401134739a8b3ddd401373ac2ccd729c0ee3ecde423ce895da703fbfb79ae9b8d73988f01c5bb668f04071d0857ee1685e8324dc271eba7d82b3cf17d1061096319e17b74a2676dbe0a993ba468e54b0885aade45d6ebeb9f3aa0b01cbe8a9e07fdd509ef2cbde46724dc6527dd24149b01013ca22ce9b5c6ba415312f6fd721c7893616678589dc620511d9ac88f407670dac9e32ce69aa3b381f6a248f62f929847e79d91c3636f115a5a060b501f3d8e94971a4423e3047b674ca523d5eeca750c6ac4040ab7700ea8bf869ac033be4a0844bb977aa3a15059c14a73a8478b4866fbf37a7d95da0c0c58b31a69405fcef02c6d03910da1a6652185146864b8c3cdf2f9b333d3701e7d77145a1bfe3f47a793d09f5ae07ad54149a153779d57e5ff1f99b3c801c1265f7f8139255eed555266cf54575a5898b0e59fc407dc065abe7f3f9e6bbe0a129d4480b5f5398e892263904d29e0cf4e0173ebd46e32a826651430d45322f8236f06fcc0575c6f5b38d5aa4152a4aff211a8a27ba79e848113eff340a333c80488903e5e9d166f326984f829d83a3f504c5e15e067fd532df02211357215421d342b58105f5a70838c9b6e94c2abba828d10ed9a8f1ddfc17d8ada7ade7b82169ad12d4ae9c835842717f6e6711f9f02cada3693f16d52b2176c167299ad610060de5d572dbeddbf940c771912c02c0092a7b3aa5fa1d9332acf019e94825a00da0fe94a891fd2ce503929d0c79790cca07dc405a1e33e731bc9acf3e815cc2ad39e40581c0302665a25ca94c4a12312de7ee6148d4cf8ae1291ffec8171f4297e51b0dfba8961199643cd931b56aae9677491ab3b8297d03fcd3597886ebe256c3123d478267267ec7b52bc117db3fcd7d03f7e94c9c7ec6455f70630d9e90582cf09ac6f274a845ccb88e53ab0f58b7a18eb9589357ee90ce97721395ef72e0c090b9db7373e5b80262a17b68261997b8a6c766c07a6c1a862e58c2b253b2a55876709d976ac1affb939607825bf06fd894d560a035b248879b93d413009146d6b38979107825a8c6a53a09522d2ddfb2ce09f1109fac35e6b981081df911696e41bb39bbfdb303e48f43bafe21e9ff664c46939b9fea7b24c6504fc520521b7b76b9c695305c5ba39c155cd85d4fd40097e8bd455b70b2381768988f70d22532a1e4493c0f8dac78758d97d275ee8a456c15870c8bb744705d9d22e37280cc7370b33030aeffabc2ff96875e7cbb8ddeaaa53337cbf484a0b55807868f60aca9963498523deb0b723d53780d05d0dc540420754dde76bc983cf1bad3f640babfdf96abd22f2037cc07cfd636f8ecd95a7d9cd538d5369eaef37bb927ee824a4cefec648edb53e0b6f15cd92489cdbd73e3f3593707b9870bc02177bfc9a2ecb20a3db33b8a701adbcbb763bb7995efc43360f610e8406cb6cd1d4b3e2b5102476bd8409aca30fbd844a62037ee5aa4a9d20c4e5d363de9ba119dad5b3b60fa2f88ba958610bf78e45598812fca72a09911640f9b0bda2a7f6f3da6e328f15043b12888ff80014a1fd51ceefd7fca6aa697be3a99d74f8cda49c0c1fb9b805bd285a7d74ad650bdfc3bcc2526abb27d09e21df14bd631ddf59031153edb7199d12bf54106d8682279dae062d4441a3d67c37f54a0f1f44bbc0e30c58f232198a9f018dd1ef7d850f4db9980e637b5d24d4f5d10884d5ff5862f37c6c8f5727954eae978b8c77b9977e516d335b0756cd617c9f262845fdfeb60daa6edef61e7f0981720852a7966495d9e706ebe75441024d291daab17d54bc9a241f591d045d5d8251e04b691a48ac17ee74c4176a689aac2621a58183b16639f1953438113ff5971a863fa09866f5c03031d619f100655e39956bc3517109ff56bc048f05a26b0f44bc29f05c558cd1a7c91e340dab26a6c344cf54a3448f1e8c7d7ced22cff67a99b1e6dedb28a84409f85ee8ad2b88836d27aa6a1430e256698552031d1de3f3cbc4052cd07950d998edd5431c8bbd950e95a651197b4e7eec0e118a0a73282d5da28fd57114dca6fce3e73525b3181ce299691ec379dfb7fb0e27e21be0f37a239a5c11f3fa764ea5231af506f8d1f52d44473918a8e9774da0fc7d26fa51dc713cd9db1460b430ed0ff382fb3c32d48f8b427573ca90c131f392a42b6554465d6c7329fe34a7fc40c1495302869d0b71ca67425aac8ee43513e6f525ae9575de58bd817af0bef9116e4349bc544b15376f2e9e103ba8509f11394f1f1cf7538075807939d2e39f20e60f1ed94b52b04d23b6c9100ccaeb56bf57b221041d2b629e7ce445f2eb4302e38adfcfb5b1345e06ad287cec4171028edfb410b0dc68a182b48afe396998bb2d9f3430ecae30e37024f105c0a0c064dd9e1c21e96994477e4557aff3b21c3023360ad319c9032f9c3280d916702a6dca9f841a20a1e753cb3a1d03658f0003faf218bf2e7d75f7f33818241f7a5db49a7de00f9e6e7cdab05f746256a9cd5f230eed544cc23b951a2d061bbe19226a8553c608b9805bcd12621d2e902e92b3efe99a8563cb8dece0fa5507c59f053e1b100a201aceb581b40cbf5d15ddb9f3b677b99e46cbf717a4765027bc1a64ef4d3bda1113575c24942fc87eb3e1ae41a27e2a02a79731b4e98f3cca0b60dbda8eb7dd19fa6aa3096ffe6737ef94ad23e123445b015387bb2533ea2c1d679404aed16013b5df890b6ea8a96222c9af9d35ff0883c62dad505b44c87ceea7b4263a1b9d20e68896fa9dc42e6e78bc9d109ea731120426e1d51cbae23d6cf2b5fb97d46c0b8474c898ab887fa0875b9a22cde667c7c8c16a17fedde7e3dc653863a356fe090b73fade87982fb63b1c9ec6ac1ecadb5db95b916ed11d375a0bef0f3ae0cb18ee143f9eb74b148d264073e9230ab50134026c1677f9b830a1208ba99fc8640621ea2ee85c8bc7db8f375e77f3c92c0a0a050f1ecb8ee9822109321486791c16fffe8a2f2f71063ac8d324512396679d67d298f8578a548f152022ef8a10aa0c0971082aaccbb2c7a216187d2ac280b9f02b715dcff6811fe8ce2966ceb3c203ece62b4bc22ca9dcc76e9346ed0b7db2b58706c652ed416f245af0bcd7b1ba2373a8583bbaea3cb69501d8076f3cc05e9f76c79beff5ea7fb80e6f7e46412627cdf41bc63367980b2530dad40fe553cfdb4d6515ac21988574ebdbaeaee4ef08abd2bf7ba4206c696626719563816420cd89786153091a72fec853577d62d220139262b8c0d07189745e2857d98cb5f41e9e3db4589862a9b45af5f9db125d0a5258d4a2ba177bafbf991202b48376e87b874c3989bef55c8d163343a969bc0216e9f6dddd347f22911aa6c69d30efc467d4b3c09dc7efeb480083df9102ce139190b086f4962b98ce886dc5cbdfbb120870888a2cbc075e4c070d1dd30b882f48fed8712ecbc2322a6d1f06a4057ea984eaa5410049f19cdcaf1726b787c404f97d3b5be1b3747ebc4f71c527365971802367c6b4abf514e538f341f2de1a0b9bd694b9910ebe4e8430ac00e653c9908e47473c8dbb9b40dbe501a660fcd42adaa7e5ae057cb2c1e4cb0385ce4a75bef2f188729c7dd1dfda68499a0ad60606ef9203e405ad7dfff2f1b285e6ff2158cc3e2d13d309f05d5c32faf6b9160b1d330571cfd0ad913b478b96e7c2f7e754986fc2a234f56ef2ea5684a98c2b2623d006ebe44511ddadfb256783e8bb72e4901e6202ef04353565f2c70bbc8dbd0ef9ce82f8259879ec63d155bf07a4794c13b45a1184c2560ddb919801ce89cd2ac8684fd323daea0b5c17c8219d1a1084a977d204cdb437ae85092ab4883e3a1039aa3e3c5fd23a480e257ca713f4fcfe653f61b831650abff63132cb8953a623e4290176176611a85e870da349725875429f87e3662a643fceb7ad38d4d35b2b10eebe46fa5edd61bee1bdb65721dbaffc8b79eff6023e999b304b268309442b45f6b447efbfc047f88e31fd425082f20e49f72401e85c6a751425a7b7782f2641975f1ca298925a38e479fb64976daf9c8d8b00b13deaac75dde1ae0c76e52b16243308be9032c867d2e8ae80cdfe53b0a61dccf4d2b5845893203ad9122f18890672afeaa0308ecf62d1c4024f4e75cacc305aa84aa00f319f376d189b4b0c1b4092b1c4cb3c45602612aea568ceda1308e83a88e8fe80997f67509cd40404295f5c4049b94a19d48f0cf69595291923bcedc1c4f9ab72b5d0240efe333b2c666c2c6f054780b8565cb796aa9d990bf509016ddff66a8d4a7c2734794d2e20847ea1967b5a8270ac2cd41f5735ebb2edd4a00a994213bed17f2478879d5d1828dbd41aa19dacc8da720fb53a6370ac57d35c2dde32e0b1c8b70df1bec16a178d177fbeafee8ec424f39be4aa12936c2c43a48085494de97bfab1d896f21526c22b5aecc571915e040c66c7b6ade7fb9ab64bd1bd08ae8af7efc6b842a8d61601f222a33822495d7599e92d99de51a89231831bb3788e82970a86a5f74764130eacd31620b14764db2c997c2fb4112f346473032d25b9bd1c0c9bd20f591d270d7656f395a2b1170879bd0d369d0989cb137567117e0bf992608eda9c923609b53e265f92c3213ff40b322a9c3aad4d9471a8bc83ba88c8a698e40e4e477b00db617498833c455b2476cd2e746ee1fe81f994a8470c1e0b5d57a04bbf556c22d9767f0be85d1d9adc04c24b4d099d3ac476a3faae48c8ba5183e7570a22b80cc4ceffa0b31dceb94d87c12dcf625dd643e1c7c565ee0ea0d7e8c107b3a7800f38e392a0af0a7611fa858b9b48f7c6c3053a70ca6d2ad176b8ee8cc2654359193b80483559c401b3753cd6f70535af84cbba71dfcbedb3e517278b781289bf2375b2e90cbaac720456e71562eb313252de60af2f645a61ea04a3e94d163cb00aa132a85d35fbe5595ae02bbddbe50c51744c40607e7d554d37e808f78e06b0226388419981cf669c9a43f3dae64e2f0531115b9028ae3a8b7fd8f8a59dc6bc14fb56195ef1217572d3b47a3ae9b32c1cfbb0c123826178d56f8abfe673941200ad3d4142d786d66e93e5bb8b6c9c00293304e4e12ab7bc0bd3378acb4e16c00e47561fef962fa8e518f98fb54c871b6a9b0eb7df17cf33374b863838d832fb05688da4eb3e9b897a4505cb15b007dd0547bd6ae334c2a78ccac748c8bd5f9f0818cd217f96809527072f3d2c241eef7bc1d6e3ba9caf55e362d988f0232d4e2d28da0aafba171e826e045bdedd7b40a2a4c40de185f625d9772970fbd121f308be46062e0e4822a7343af7efe500c02ee6db0abe31b7487e8988f2bf8689c60b0bcc2bc10f14292ab79193069b345490a032cb8dc9edce9974a5eec218ed281f25f89501fd3dde0f2640b620ab0e98958b5abeac175e712002d0a5b0f8f0f900035dc00c007ce172e2b2400426d0545968f18a657b687d2495cc0e6df742a7128ec49c7c7316da38fcbb03c70b1adb709f43a0220100a087a025c1dac23df3295dc4cef636221afa43b7558b8eeaab48c8df77d983da654b0b8cb9e4387802299e63a2524587359f6f251b1c0c9d9d909530d71315c54c836bf6459d8427961561f2dc1fb29ccbd03171d3e77a9e7be88573cb68be89a606c9364a6289be8e2a0d507b4399ed3a6e52dd2def2a2a370b247b27dff31a90c4f2d7ba75b95c1404150bcc43daa10dee7d5b126bb2cbb49cc849f88601eab8c70e94acdd8e118c2ef804608008fe18173850e6a014b052f0d294b9e55b8cf15a98aac260af355a0ae54eb5f85446bf3531043b553f86fc94756e82e4d323b33ed14a74b095f6881912102e3cad528016fe7971f4fc4bafe36e9a45e06cd2c9153e2459d028badd043f35f97762238ed1a53017ba188e8f2911ce6823bc4b005a0afd691ae2866724688f7040d03c331e40903034145cd9eac7115c4ae669509a0ebe45b8a156fd2c43e4540c6fc6d6395e206e073f5c112c247d98c7bed2e88b1eceb1b0a32bb90468c81e2254ad90619442981f4fe119372caceac340861ab3c422d8db863d1c18c118a77a3e50a8ecd4de87e4292decc92a138e62b6940d0185896123947994300009130005cf89c3f10fdb77d2d77c56ab353e962fbfac266d97c7377ce04d27f3bc5306c169967a385992970ec25770e20b02a24f4764a55d0df286d0c0471be008b18ea4ec1687226e63f3e36e8dbc2e67ee2668ec8e013afd9930a7c42e095c8044a262bc7ab9df877e2c06acbdcb2edea07cc85137b4035a8c9eb03f57261bf34da907142071d5bd89bf1d82b1a940d3a439e21789925d5e42748749ad0ad6f71de01d2da2038c827eeb43a0db8ffc7b10a9d07f9b9aee83793c84018d2f85940cd840b57a0a0b70b1ac2021377a8d2ee518fa68c47412fad7784a500e172b578b0f04ede547086741ea025f347a4b8a0077772bac59f9903675773cf0274b4cd65bb652fc10a24a646820e0c1408177dec93c2143dc42186830753675240cfbc8c49cacf2e283cc5878f9cc793e31b8d81869ad0fa022fe2da056a0b72fb7888206401eaa082f9d2614ee8f954eb42df400a4f5de1390697ec43ebf2d287220deed56d8c48a8ccf3a1c467d5d6058ca4999fdbe743f020e2197fb337c249b25b550ef621a3d019aeeed137bdae1d2fb3cd65827f19d101b5347a572f220d9611bde456ec38e8b8e666cec80522f4600f7fda0417682337e27bfd4b9be033efaa36d5ef4150082e6f1d0160bfd88a954a28d4fddbf77c0e94d6f9d6c8f2e3a83f659f4e9c0a46d198e9ba5394eac2743cdd720b1ece0d9abebb3d80d6b0b9ccc93d8bab26af330f7a7e485c40081f5f9e562b72f4f69103b204b93d9a9261b1f811ee90a9dd2265b00beed7acd5304d2c978eeadafdc8ecdb8ea2080592067d5a8c9edd7247bc3d9c70076cdbea4f04a91102e7d5d89c678f7463aef1f1b36498a303e8ad2584265749bf56f6d00ee575bad966a4f03ab0ae32269c8ef13fe4bfaf3562fc4051fc033c79a98cdc18396cb194193d509b0567216fb374220e93d38a5e620a7eda7a0e648961fbec4a314009117d8dc1a539104240169f3", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000e40957462902168235249211dd6ba6ee00000000000000000000000000000000f8defc3ae63e7ea8b9be28634acc72d911521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da069abb3ab89cdd02776ca33f3ca6e0d10986b7d8cb986d0f7483b530eb616eaf1ff0f767423d4fb0ac0d17aeed5ea083d9f2bcc677df5fffd068a9470bb0650a1c7f0980ca62990220ace07c9bcbc630aecb375879e49eff85b7b847f9809772003f9b2ed5d6ab5749b0ece4b4aa05407fa4ad7f08b38aed610a3106e903bd182a2e15e0c5d25a298fc02ff12751572e031a6b9c5317a80e14909d881df864a51d05b35693c3c8815a9bd00583fd0cf9aa65fb3fb6fd405d1fd6696f70a029c72b1bab7f7742fbf2e42166f7c9aefe3be4a66c6f0b54f222802f6d1cd2104f82" }, "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000040ab06fe21da21825000000000000000000000000000000000000000000000003d09f6ba158cb416a00000000000000000000000000000000000000000000000653ab0fe8456da7cb0000000000000000000000000000000000000000000000000000b0d51ec6f54400000000000000000000000000000000000000000000000971f9a84520aa31270000000000000000000000000000000000000000000000069a8682bd694d08990000000000000000000000000000000000000000000000004aac14ee1f1f9cc300000000000000000000000000000000000000000000000000023c403e139d5c000000000000000000000000000000000000000000000005840763058f90f862000000000000000000000000000000000000000000000001af162e6dd3d19c42000000000000000000000000000000000000000000000004606b05f7c092197c0000000000000000000000000000000000000000000000000002ec3674acffb5000000000000000000000000000000000000000000000000d53670d6246e537900000000000000000000000000000000000000000000000f962e8b1c78199d1300000000000000000000000000000000000000000000000a30b52d4914d59ed9000000000000000000000000000000000000000000000000000115258cf5acef2743e93d4e6374c24aef3bebcc2f4f2420e9ba7a5714ca96df52ec420a5812cc18aa6c4a565a8eae50ecf4b59ac47f274431bbfabc786534686220220083c6d426a4521a0e9e50e463baf8bba7d862eb979fee57112d10d6739cfbc5a6fd1f9512071ec987928cbd4fbaf9d86e277597b28f7f28b44afac1d78d050684ee7ab32ef47cc61e6570347351e7d14d514d6aaaa183d8f3136db330f81539a5ff03c021df9c271991b7567f82707a8ad58cc0f08a854d6c66f37ac04f4975ac8b16fa235b7da200d49e8945b4239de3bbf5257002bec0d02ca95619441e75985ca786043564090305898166319bc14b2ae8de7f23278e47910d4af9b22c1af6e4d6772bfd7385b9d88b321d75fb535fb79e9653cec5db75ad0ee5b430d3cf69f3f2e42a8c8cc6a439e517bcd51609fddca9af121f15696115028a34f01b99431f8d2d084c40dd7a68e2440be69d3c4cb4193013c9a52d743256ce32f3ae7ddb9966c82d4e413c9b46708028b32614768bf0ee52c354d61dd7ed7a6c07b4b2160877f005217f14563fcde0b9affa023d3ffc28acdf88fb52a0ab455edc3f200c41d78419495bf2c53688d2c38eb25f9b069fe0fac1156ab227d167346d68483944b5cd02d388139006df14da7dc59c66306bb63b141ffb44b153d3a2ce2be113a26fbd0f39043bbfa987f698498492d2dd5a9a107e44e8c797477789319d1c368f17d12e2ce36c2407c29941b527945fd15b8c65295e960b553088196c5c3efe8909610ac2ed3cfbfb0d8f6b42a55b7d96c1d6b876bb7eeaae3a52cb522e43842b6cf719af9dcd59ec0090750f2b640dfa3838c30b7fc60d524f21ec41e020f83136ab097d308477adc85664b412502e9e074c8ce86605f0bec32cdc0a92007bfacb4a0724afa887cb10b384a0f8a2f435e5a6a8a2476c1a43e794b5d040de6e17d96705a97dcc7110d434ef35baec043f75f16a8ad2a65ef85cbe11992a9cbb31614b1ff0f0f3ea28e6d81f4eb1d6f1370ba58e2534cb627378b8b69f6daacaace2fd158335faad367b8a5f88eacd5165afe29486713ca7d4d0a797d1b4a559d334dd13639843bb7819198343f00f242d17f223126fd1719b0817cf7fd5687b15fbd2031d179543cc51568d8451eb313243d02cab952c0817f78098276b027c46037f05df41d2b0c7bfb60da2364a6be4d4c6efc8a3bc3cfb91bb7800175e8a7049322469195f5e50994463f2ebd5690e6dd07daa9e797b5ef4cab2d13985f03f8994232deb337dd366766174a6024864c57858399fb5aa60ae7ffe0bc4f60558e5fc029d082fd155f55b3b7a1d5d11a9cc8d05db1bf23bf81781e0c95a310768386f136393e0465b8d7a920b5f46d99cfd947040ebf2edc4c9b56515fc197e7edb320d5ec04cff4e878dfda85a4d833aa29c3732cd973cb57044f72f28e220f086fb2394986b66cb4c5448a2e31fa30250ff6a017e4972a2178aaab76d92d14b59841bf47a8f314c8ed5cc73529ec625ee72879c6c70572cdfaa59a411146f53483803b645f74ae55e77770167dc9f8b9f88352d5d1405f2b2e1793f402ad14e0ea93040f6b5948c98694398711f39e7c5083d3a864d51f5d18ba87dc2604e5328931206b74e1af900f25a6159e24249b86e36c64edf56148d23e3e38eb6c415256d073fb0322559c292ab8be3d1bae23947cf6859296a20c33125166ea1e5db893200c783848aafa0b2076135f23aed7b2f6841c5d864e9ab0319b223b3df0dbff31c4ec54ef7cdce4533e6d6646188c6e79e0c62271353827e0cb821a320bedadf09e16bbcaeb45335a89d7dd4053c17ba82a9e0240b3fff9a7cd58af3f70f608d2e354669ea4f1ad977ad4ad13ababa66c1fc2794ee78c9fc4faaf890c9f2328018cf65cd2daff5027404b5baeed49e757db29c96fa3219ebae1a4a1c682c85131133f4d09ce60ca38a9cecc6a345a442cd11b6ed794e3bababd71f35c718c3b21a461b3893f154353b00e3e5c85832e6737f7100a9a5e29e421d31ff3fd1acfc177698f7e90ec5658b4e542ba9d4e202e46b6c576a1d694d7dc59c02236d052a098e5044c0f357415a36fce6e34465177fc0b914d8004ebb1ea7d7cfde7e91bd08cd0f8c745174106bea30b2acb002d58b77a984a681747c096470a038878b2619747a68514f726714f01143ba33c6067623d8d784e916716ff2da9c40f34787207b327f55b17bee72826ac64f91c43badf86e8117648079adc97e81059a5ed1283696ed822c0e5b0728ba6156a0636ad29ef805fd968b0efe04e72d174bc4dc05d38ab82149ca15c737f244b7c198deef75536eb116a6b70c99caf5a411978829028ab6b848af1a7bdf7240b46e78dfaa08f6e20a2ad9d72aefdb9b657184162d1ec631077462701554b4917f1598235d2fd2e728a6296e841f5109749dd4ac20add72426e1dd53e17d74de24f677e889c8dc52e462e9fb12989153424b619a168ef2fcbf4e23612d222a24d236a685e7ac1dee99e55a15ad5c5ccd1a90868c1f16d13b8af94831b9d998517f478b12843e79b2cf8b510e731df1e4e53d85f027ba9b775ee38e4722c1c50d840f864ad79d5b7f211bba5ace55624d9d67bf272750e7e77b265292e81e72b769ac39c69d6b87e0abf8a12905291bab45049bc9003580454d1f45cf3209f216a215f94e8fa0296039d960ca361184adaf05a5110ad6e799d393a4ec57ac1b496f0bd4c2d2d0a21704d2e807c02be4c0e65f71861ad74d91e1daae0ea9f7da1d529adb5387d14c79e9c88b2b0a5ea1354a2c451726d5453a94e0188988fcd6be18f9e0daaf1b3b4a4942c4ffe4bbe72a071ad2500f4019c931b7a61459490286f70ab69c358111ccb1062c984dca75e9be31d83f25a3f36ec88d2dfdba029caf2b502ac2d27ae6e2e4bc2c51310b577be3097b1904113b78cf1b719ea164112ef6f06d8203f3fa089bffa1864bfde3cb53dbc70d05f2887b68f9ab5de5336385f3970ac6601ca216e60bde4fb2727359a07121df2d366ccdfab360ef1f550cc116b3c4c12260c44d115662e16480f4653e71c601301100a4d931b185a9f7237ed93ffe377fb2c8a41ed83628be99e96edcf0ee871fc497dd70ba7658fb904f63552ce36737823d94cb2c86b5d043d00771798b0e137c6b475a99e2fa444f6249c5faded17fd2593e841741993be5e10a0ceb3fab13491b3cf515f2488e1ae936799cf3171105220015ed05efd0df77b02b685b850d4dc8e08ab1a1e4d84a89d55c2009f137b071b5ef47b817e8cfcfdfc318f7ee2e574cdd771653fd4ff74f23315ebec3e844c7fe91103cef4293ec6814eef68628463a2acefb7f863024e93710531118059348f66c509d87a173c331a959375d2d484ab1e6ff5834122e05bbbb8c5bcb5a3cc4f940e46e230bd2eecad42884042ae070003fdc0787eda93dbea86db1bc786f91b8124e8bcc48b53b91ad2743f316cdfb4f8af4e985375743c1ce93ddcf33243d2a785af13054b6f0a8e2d7bdce2c0f57dc1c3c616fa0cb441100f067dfdad8a704b9409c752b6a44a8192af35104d6a676ac93f8ab16780b7311c4d9e09ee52618cf47f75ddb4debfa560e325f13720280111945493a97974e573a977c4be5cd28073fc26df9e47a030a7b3cd60a9d160bbdbcf86a754657011e6221e8d3c0df4cc4f51233d4a587362f34660c209d0ce770afb06493a48ff0188e7b0f2bfdc3290970cfc07ab046a894581ca208af11ceaab5973914f6936e5e1bb5c7171e41a83758ddace922067cbda7bec528a70f53d8652e346f2811637a2bc348fa4a329543ee215284507e18213680c01b87d158aa750a942593d21665b070248eb05d9d99a3e718e83d051b80b4d0572cd3caacf4b04d9b02e7b5d119f825c51ff746f1b90690fcfff820812513341a1e518c70665d56c4fdd8026696ce962b244efd18337beb6d851371691ae548b0134e21cdd57a574975f136c4c9df4fd47242e4e8d5b4d4d5c64d434b7dc224f80ece578109ba28c79b93d58ca355122180703b48e1e65bffa0e4b8cd0834892a1af3698b86a6f19f95d7bb233b3da96574a99ae4be95be5c6a1ac80cecbaf4100cad09b438114a5de5e688f2350b6bb9628a8d529f64a0d03495a940b8af841219cbf4d848305edba7019532ed0abd817e4351dbf89985edd837bb84a253897f0f45cd9cb9704c95846fca3cbe168336a7033b9e5484e1ffae1377e5bc24ac8604d341ff988a69336a5227edf5f1ebfe4c03b8080006f1734bcedbd13c88998b04fd88933744df41cea43cf4364514c189bccdae52b745765d39cc7323e0808926e73ff470ae40e1274dfd6e4f1a1e43d34ba15f6cf124dd408631b349b3288d1bde5a64444aa6d4d969b48fb876fdab78c00be233cc6d7c2285b5d57e0d541a215591b9ba63f7a1031540a0fd2ef4b25c45401435283b8f5714a6593377fef92883a753b19056b11835d43a8dab798ae41ae19bddcc9b77e4a7d62a9044237d25debfe3113d1c32ceef3f28f255fcd7d7c1cc926ef05eb8fd38452db84907580238852d6123effb342ca4edc2e2e6cb1f3503ce645286483a24fa0d5a5203cc1901f6bf1750b0562f89622e7844fe4dc4df62f393e2df9ffc367d5559fa0eaa2e9d24c26cd530248c3612b7b92324fd60e9f2809a7714a432795348dc35e1b6157cf914b2e55748cff5db38d153c4bbfbbea5d69640d6ee322b2486d2ed27b206d734392617d64c10f7e920f8b861f2e2ff047f95f64234ab271cae6257fe270956d1443a7ffa4ed7934245c40547d4f1849536d1a325f89ab9c7068a918795136bc9fc6e672be03de71c3480bbbd5d86b9bf3271aea5ac41af91b48c52898c165691c3be42b9f43326f98e9240862a025bc5635dc1c606fc0f2509d1efef1b0fc08de20191f99e02b70875d3dab5664655fdd5290c12d141284f03b5760975043a240b864666ca80bd0d9834ba077440690d7e32958bb09128d192c4a882d80291a7bdc10b03ac9e1ba37e18351704ce1a60bc4cbdfe5396fe6973993ba8a0217669bd2f4ff5f684fdf64f00bd579194d190e52d3404eb217b7bdb07635a8704e5e9cc2eedfb4d5eac2c8c97f99a67a70b722e722545ae14a382fe970a42ce2fa136aa4acc0f44847caaec9e9b5894fce40b03f53e879fce904b96b2944c3219a345407d3b0b4701deee680e0407a6ba609db5af0239234e3038e2abe9573d1cddb19fa9e9f7cf586724b8ed082e215fdd0da169f6bfdee0c722e449c65f262b2aec80e8ee480b6410af0902bad7c53ee032e7835fe9dd4d3be4567195df882e777b9560db7442ad719eaac80f50ef874f2c54e279f3d54624ee02f47fa9501e264fe1f239017e8a52e0b02360126fcd71fcf44f9aef62d0eb47d6a2ae41e72ccdee6ac3d32aea7fd40afc96b019f5a9951a0b411e648fd40a41c1da06214401b6bd9f4b96db40cd2e3c3999220a789265f0d09426777713470380bc5bfc5e1fbbed783e3fec98db96fbd0ba69cd290c27907ef7b1982e7ef331bad447a1880722f5b7cb793fd8f27b9ed1d31dcae36e2338a2131a928c1d0e273db270b05e2f9b8b2e7e2762b41e67be8ce9aff152d20108397f3b73bcffa359b7f1ef0d4a2e963a4898c66d5706967babac451f8be3c3576e914f059af6ec9aa0952e5753019bd4cf18e64290f1c165856c17f0ba9651543a42a2ba95777c40f383d5d5311e747e15b6bbc7b2270975e0fa26e5a46291cc68074be35e4aff55b3e5ab75ea0b86348e389c8c5fd94f675becb39304de9b7aba1f5f24f9436542ac7270e91809379260ca1721fe623ed93c31cfc73290184e3fd5be777ff9748a62635aafce2400b92b1237ecf7292a17c5ac3b3d49ce2c996f9be7c74022ae9328269095f4261d23c6956f89f4d53b4dcab088be3fd495b11761c7738db37914e27d4ae9a02f40c24808c10c618690498fca7f59fb1d4ad4900940178b7cfd3c73d8a0b4a6299532a13a9c4aafe6663eedc2abb30f5dc5efd034730725391abd53c9b10d0c2e83c607da7e98c97acb9a1f579133f4cab156b61d5db4d14bdbeff7f42c65d419deb1cf3760699ead370c8b82510c0b32c579ed97d96a225c1ab0686fc6130a074eca7416092782b3271155c927f47020e8c0f1323f683cca2ed574050f733001814f79d7e5adab32768514f5804dad018d2ffc2eb9339ce639e4217b7cbd59290c6c55c4161c0fc95458c7b2caab39bd0ae622b1cd31a6facb5771fa91fdc61b8350d295d4a8ac29612ee0c7c58393f0fce2304cdd04e26b903b989a890d2512f3245c84b74f70e541548f1b43103ff12d4183ca15f6c5c4953149159f688a066190fb34a540cf37b4b011e5d56d702be708ac52a6381e6c7c14fd5e2390bf0f02054dd47b7c3130ba3b05a8358213791d14abd0942f197ff41acfd49e25000029febbf849756d5b0a1ea245c8e72a8ff5d8142e0dfbcb03d081a1e6aa523c0b9be309992df10251df3500bbefde5e398900b31a2873400259434818825128009b76de86ffbe9a22f6b0401cf2a1d31b9bc7a467edc49d08bad4a3be6a867e0369d0df4228f24157e3e74bbf98cc4fccb350cc0b1453985f35bd6d59eb3bbb0c8a708fab3e36564c0fbcf59b52b07d420c6c27b21dcbb404d78ea17ab65ab22a7ee682d495e473c0b1901da9986e35a66d58b90181b66c648ae8a057a0c65509d48727dae811979c6a30df9bb90dbb3835baf0439724bd6a16546f888067321026e81944747f1c22e9ae204cd1991984a23fcfd5310572b72d74248c91be8f1d39a8b44143e8e86c599d26faad8bdf1aaa2dfe0a5a6bbd420fb8cc4827abf30fe2a9d0839b22588fd2a0dc6cfb92fd6f86afd7f9058f6910f99e65a315a0c106958952ea6ca6135d6b09f6e67571bf4a4e4641f96965dd1706c572337152531aa504225fd8ef9d54705af04553d536d3dcfee38b3de834864252be74304ea8088fafea00475b0ad5d40f8ceab5961c9d626cafc84b26f84400357447687096293abc23a2e828c5c37c97c38f866911d9ca984a03bfa86b291bda99435500732ecc4388049288b6b777af9d4758126f2a552bcc16e75bf97bf596c0c497237f1c9f0b1ae3d97bc4531060d3ac576bc01cd9d5f10e137030e4bd33a81e16a8221e79ae12a6f7971c03de925cc87490f9b64ee9db4168f1b03d18f286ee8efbed28f9b5457b522f7446cdf3390c66ff86bc466884af706dec76517f9be11b1c4a24ad702d86399352c2f7ad293ae42d8eed275aa8ffb377b3c4f2d547deb234e62763ff0f3e58cbfdc39452771c14cbc6c8eb0e6911d53ceb304a20947766d8b415fdcd1099e3d5ae5021bc943de2cdc11ab57fa9b5a6f6b063ce886aa3eafc5d1a518044009fd94014486fc043c811bf0ef250d0ce1a073653c935db8219087c2840edcf89f08a32eef607cab2cb9140e18cc0cdcffcb023e0b26ac6691e290e01def66e7b0a31bb1110291cb92cbecbb0a5641fb9f175671ed9a114b757acc6169bebf9c9bef1c27372d5509192f4dbc0999b57d6501f2f4e04bf5be97bfb33138ccf7f178db34be5d750cd2bfd8a2375ee0ac34b4410896ad8875efb8d81131f2d5c591e39c8adab09adb775f559b695f390e934af08c8182757cf340407450bf3ab02aee67a410cff649e08fb935e1f6ef6556706d6f02464125ca1c6eb8a2db6877fb4822ab722f3b8e9bf03be6078fd699fb51125971b53a39616de119605075a795d83199b15c0d103f0c91f11395a517d1c83340c2c91aa42663d1a3e071e4414d800496e2ee7d8551acb3426677077be3c35d7f41c69d4bba4606ef7036ae83ef1b1c06905b50b2fa351b94d4be937016f6d8533368481481ca6fda30304474257de875ef5168fc4565468471c81b4c65719d4037d7cc7c835ccd80c01de4d6a992f902e74fe1b3195f1ddb53a672b331133175443811b957c1f68090dc846cc9b717234f1aae65a03dc471461efcf21e69d8be21937dca5aca848c90b98782989e7ad92e85b4f3ae502fed8f71ae2286d1336ac0d32b8a33de55273139cc206d8ffdbfba5df914eba7251435de570b239374f98276811d3469e45771db82c1754c29ac6c6e39643b34feda8dc704facf77b1eff5ef2fb8e7aa71def11a5401416d16223009f6de585be285013d644bb03ee8d9e124ff9ecd139914a2d37d47a97ef260781595c5a33bc089bf5b870b31c2cbb6e2cb96551d08fe1f100f2311a43fcca3ae43aff407034fb1b3ebd7b2a26127be42820fa97cfdb8fa02f41c7e12202331d288ff905e07293cde5fb1b4416853bb5c52bcc2120a445072554185db5bbc6c13c023e10e818333b3ea63907bdcf0275688f4480a8727b2f183dbf9242fb2dbd3320d2196863f6fb29fda90d4d090bb3afba4a59b2c68a5516cde31823fcc0d15ffd5476a12e54eb6c38b28d04a9fc2bb41e13ad2f1f2875138bed8a04299e7c714f7413e5f053e37e304248e7d969a4f994d15410f15c2b1d2aa31f2d70042e1d242c15c593a7a846a95fc96ba029425aea988212bf900e0e9b2a97f5f8b3549fed8148d271db03cf878b601a6e694024aefcc8ed72093f03a135b5fc9b72df85fab4825c6352711b7a71fc9876d66ffa5949500a6508c52518966b85d42ab90240e68488817e62085f222850ee38137f65ef5173eb9d341f47eba397a1ba71a4509aaa73f535b5e2a771a99a080ac81c2632a3096b287623a2779126dafcc6577a643972a6922fd3eb24bd3281dfb6dc1d19cd224a269f013a2861c8b8fc8036d89c1689e27a68ff32162685f45ee516f55279ed6b32372a3ca326977ec6b34cfb295fbc099ed807c2284bf81a78546681214ab14c4edf113efb51ddbbb7189ce15790f96f54615db145c0ce9e5e818a59e0ca7130a6921b9ae2514327086240dbee82447f852fbd5e599af80db39784af850b88d9060026e9833fa6c872b2c1372f93099606837851a1a168c48145d9951640a97b72092042dee789eaeec2724b826000d39dbf75ba4710212bfa31068bec37ddc16a64050095288cf1d25c9f9489012d3349074eec75085803491ebd8298924143273d1c498547e97386e641295ec8431522d17cb9dfbd2f2b4217cd83c808c7c5f18d10737bcd0f8950e8b2555b78e3f78e91834a37af5386fdae725e3f20bb54030e189b7db9b6e8ec5eff63867cba626e652be6c46d665db454445f0f83f08084af2113e5da93928627ef87936d16a7000a0a094a5002c5735caed7f84e96fc8af1114de25f98cadbda11d3a91d4d182424c842fddb7c69cf66ddf1ecf33311c9e91b198867975fea5bdccc98d7e30e8f3c8d8a12e2cf1da8a91da8373491c658ca0ac6e1bca3d1a63e41cbcb27336f3072f08b53d48c7ab0d0e077e2aaaf1ecc0e1c3898c8633a490bbed7d87f0b648dc8f0f18c049f79f8510814b0513f9716542796fad753e5425f502639f66f85280d9a026609045e5ce3d85d58cddfa0760f2da4146c8424d471d5bd5992a8fbc67b3bc24a107b83575ea4ec667c8684d4a50d879310cf292b4176fd2da386d2827c6a949c75f45c1af3841c6742811f4e532b67908e68172683560e3ba9deff96afa53543c7525ab1e2572c675585600a3c0e8d9cccbdf1d2dba272332cfbf5dcdbf4598ca883d526f0348b407a0b53b7f72d63519b8a3fd6b14bf1bbe4d0caea0eb71f6303aeaf5198f0b96c540561c7b024593b5c3bc10bdef4eeb418bd7d40e811b46a10d85eb1d03201b1174418ebef2f8d48c74be847aaad4d92bb1c1a4905fb0fad9118f778ded6e9f18e291e974118d6dfb276396599d87ac39e61fbbd9acd173234149be49b533d3329a6a471a00d06013725130cab4546ae6063008eb76f952a8f7b1d4bc364948346145a1d512950eb1ba9ddff18f9a5a6b46dd0375ce99fa87e59b41ca52fa1956a5f5b54851cada72169d4dce1449ab2efb0c6b9ac9e152462a4b94938d0e02d656b2b302516047fd71cfbc6fc5f71c30e004e4e77fab46d560bb462bdb52508506099fbda2a132413027e013ca6de54dde3497cd6f4e165855c4daf55a23da099c2ab11422b7037249598689857c3181330d7c7c0c85723bebf2fd38ab79ff15ab71c736e070cce892a0e995f3eaeeafa42934a50cd1c3f68cf168c50c2541e7722e08b5a1952dc2def02fc665ba4ea42f9820ec05e6f622ac1445b710685b911d67280240ab866edeab9a93f511c74e06d9409f43ea822de953e04b405e4e258f3cb641e1d51e5848cf19ba0090c881d58acc9d09c8122626a807299cd251dea77bfdc6c1db5a608d1d7f90441ac3da7a850b39d37ba055d46822b7f4a440b369ee3dbfe0e2e4afb5a995b21b025423a3421ea55f22a69812efe31e835d00ffbaa26115c1e6fcc35e3bc1ecaa89b717d2acd42f0ace83fb1e88c08230658c64331a0d3c002205b52ce6a9a3afcfdb7fd07f0ff917b0682a827e6be0e519de443bd7855c028ec2309d0e2f5f228b984c5874b5c1ad10d7bd97a10bac7b9828e46ed4b5a281205f36b20d02c74c43cb5a415a3785ca8bfb4d3d4d2ec5816901120511ac4f82ddf777d020409db617ffc7d064710afe1af7171ab23eb3a2827242a81ac15a00c068ef21b34310cc14da591ad18e6e044f3eaace0ee07bd05ecbf722a7b6c292bc6f2d578c6d7d9c998f997dfc03a913dde3e1d965b17f32593a5a0627e0c092c10fda42bc30f3ce904e4c26eff37953802d60afce5cbb470bb6d15412e268f3023a4042bc161e38fd8e621f9d459f3effe4110dc22da5fa1868bd3918cf7930d18599d713f0b6c205283389b0f2453340975f771defb2b5e31c3857dc90fd92daf36046a3cb64afcd9050f4bbb9fa89428624a066a44dc8693c560b09ba22b0a418603118fbf9d862084533831534afd2aaec3cb75aaaf22b396d7188bfb0e0b3709fb4ce0a08b7dd002572a13ec48a5a480dbaee3570d1fce629dc5d0e56b21cffbd42d18cd9b62dd78c23ffc8d2f5ae1c467b80afb45c687c9dc954e8f321b43ddb100a18f3af3880a51969626dc3e15ba3bc4980e171184ee3bd89dee052639becb1659d8c12e42e0652b6a33d148999ce6153d4f4e7afc0e640bb231ca2a59dd79ce91efcc51caf5495bf45e00df2beb4120e7d30d2f5a0446206fb6e3196ba3c835908bd1a2bc0754ab6bc4819f2f5210fa494f76792070e5e2c454be02b1da704e87aa8489ddb9340f8841d94446eb311f21bd78713ae2498aaae1631834d8fede4f068b2a340d75f9e37b796a0d014d265179ae4f24c0e79811981d2ef1b18f66869e914aef1780d5bd351e58eee8423b18cc9314079f6aa2f344601c8b4a05c7631d77da06ced0416667ba392875d9af73a3157ef63673783dc13826a144f350975fa8223473bce2d7337890bd3cf3e56eccdaa2928923b06eb9f5178761d6ee4c5c09d2c2970c06cff0a9baab2d0718f5db3f61455817fe60a7862f8573382c9e2d1eb09bdd03bb2af0db6344c045fb86e12579a8205b460b1c0312cf88883ca725d6b7319535c10442a31afbb47781e7b486a30e4c35a016770c23790213fc854527c7dba54a75a9de6b88d741433c32774d547ef5e9d86d1d76125a9c41bfe6c1c5414b4954c5ddd09c8830eb22edfa6888f76e6f227358ce321c8233821ebcf0fd330a038028d95c8555be7bfe7e30a6e9a95934ca77461df027d3e0d2c5716339668a42412bbbc5e10dd51fe22a7ab706d88361503c99d4ef0b15a392a838b5c3b67c251972cd268b60c15d3fbbd6bebd225ee236d219d942076f9d9bbfb77981cbce154b5b9b55a15e74416b9af9b1dccc957bafc744c124277d650e13f2839ee6faa36b0a3b4db8597de30e4b95efba2d6aae287df1c67b1eac34ac25df0fa2360d47b727bf22d4cff0745872825394be099fe22b57a12614ed7350f7e16da6c81c07dfdec5c33efe7b6486e06b96a4659ec6fd8671f6a82c55903d7c139842b424d91666571c68928cea428bc1d612d6e7f44bf7c27d0f17ed583db00056b3382cf91233026547ebe5f93f1aad6309cfc2e419a7c179520e99025f7f20d9951f7b11b0319944d7d18c1636bcfea5f034be45a58632372204770cb1ac257d09f1a6a07837916260885ea279463d727b331b264b1f0b49381f7f2e77f8cc6a69171a6a7ad635e415ca81bf32489b92644e873a4fc170ffe1117c1dfb8f1d10c543a1d9047564d769469816930e3f41854864298374980cde254352efbe0abd397db0fd78222d71595961d9981c996d5ef96383dbbb766b840876fa3e4dbfee7486a0199e40a93774fcc202ca7e094b1bb99bb5c03d5128a626564e425bac7f3eac2991821756e078e406f16d5fa573f59897d0d345244d590b3d34d64df3afc81bacfd30b9e8164ed6e043ec5250231e37f9a6a8f652bd7f0fbaf17b908d410d6d947d7fca1d3b51ace99f755928d9aeea46034d972a296d25beb92b01ebe662acc722dc0eb9b8ad40227e67acc989bcc820773e1c83aa612905aefce55491f1925af1c04f0f231be3514324a179e5315a1bb37dae95a5661f956272bb735d7b95e89c33e7c01be2b7e83582ae1a355e9df4ecc315e84dc62ab2c1206457dc7ce8ec99f837075700053651b11109c8627bbc53705b593bd91c3da4df434d327d486cd25a916b8607d33f31ec6b9e9bff15eed344c0fe88280d877a5b4bcdb00799f7837455cd919fddd991bfdd02dda98cfc21db8b9b880e0fe664cd7591257dcc7cc3e0c131721b88d6fc684e93073ba5d5b050eba773ee1f1917ab154ba311a46e1de3a8818934b0984ceb63d6f8a0e8fdd1e88f8ccb8521fe6811cbc580e23c1ffea1b329d7e51865a34d1c0fb717e870a221cdf9390a1815630d082b4327aaaa31d4e2c343fe24db2ee45cfb6209e7b3f052f65ea94a05769a31ac312b65e8b702576524d2e51da83c3511a1b211e67cf750a2e2a63a003f5e68ab24af990d658fa911e9b16d48e2dd4c39a5420208eab00611a2952c14a3adf3a46fd71d7dff35d4a329ae6edf88bf82ce63767a264c0f55f58152e12f9215f4f60bcf23a87a052d4ff98d4ea15397227ef1a31e27630c2598afeb781697c705ff91abd6ef567672b21af372848a5b907b137380b6f35578fa10c0442f8e8a9c1185d5390101051a7bcb9a9cc26e54dfc06dce51a827d771e712c4e726a462eb20b0b67b110f61c511ac1d1768c17bb76bd94f4687e926e91d8d326a169341d5a6c63d0f4d2a4657014e2d32ef9e2605e83ffff451efd4e44c31e4331cd7e76c07fe587393bd2896c20f496a1284126981e6dd39c9a93489dea6aad722589bcf9152050f6fc89e1c9bdbd1cd00b14c92dac4b0888dce9deb64d34b6e2799104440d4688bdbdf16aa80a0032eae6ecf4f5457dc7ec502f9d6541e57f11d0a33df49cb75b224d68297662a24fcad6c6c4a1d058b66b52e5194be351854214297bd50145c5117c2046294d80ab362f32cefafe752aebb35e18b6343202b088a5e3740209e74a2dfe097771d91abbf91821bd6d8d2775370bfe80c3b05a20b28e7e4a875a7f7fcfb34543326cca4cf25a9711579795fe1cc240533bb43bc25531a47002615f2a582ae9b97af5ab449ee49ebf1524fc70284c5db9d9ab8d60cd6a756b5b14120ec32650a922f09b9ff0e05191d9cb64e8a49bc9ba9ff38862f8438c9f72d8de501a998b72b3e094cee71964c093774e6cde95231bc7488af29ef82fc95f20b73e22cf7397dc2f815c98cc0ab35ce3aa2573b3bfa52c1c4e20b14ce9085edd1ba3efd5b4e7eae79db51e0c0b6eccc2d8c2fe017b1e953050b10865d5f69252f27099e8607385efc6b34356105e0d55711c256623bf827cd6601ebdd0093ccf8b5b7c22a4a7c023a8f3f6f9c2e033af6e983488fe17ef544b70afbb9a6c7baf14c4c66d617464ae680959258f3d6d0921d8220bfcdf54a201307980f3e5d8997783a5cc6260d02bef3906564b64e944d925cb366b68aeae3762bfb562400014dcb231e337e4cb1835d935a6ceef1cb72c2bf7ac05b9b7063b82b65f37d1d1f7836b8339d8b2588c630d786c8a0cf090161b1d37814363803581fa5ce82abb747da6d6c34a0260e627bebd1d4776e9e328fafb6d1383fb9b2062ee54c659b246a40a81b5eb1901216be06a9a6da8aa2fcba770d5ce232f8bb551259bb0dcab25477febc5d2db3124082c3fa025144e7ff3a2f56d6ba87a7d2df10fe1067d51a1356857e5a675b99f2573747dc685678c1896d6d820d5fbb6a142365320332eba438ef44aa656caa7815f18b3f9c59eb23e8b60deff463beccff1f48996cee11a6a538526d034a9b60f065fe0f5039bf073640b2ec41db6fbac5193ea88028976c924952c5225f55aa1affac5e7586245ba59dd8a2df0bc13af700e67b6fd740d52b6bec53ad840a94f31169d6dadbe51fc56a45b3fb95b7363e06027ec79d84a0751501e4ce5afbfd2be1117af68a171593a2ae7f2517eeaf3d0a26ef542845f9179278f756a8155454f79686846fc0527bbd8cd9f42ef1287e06eb851df7c68cb72bc28b11fb26f5a0302f4d1431d9bfde843ef7c022000d9322e65464fc3fd94083d6b1d693fc9bcd311e078cc6fb4bf837c86b1336509b0c03785ca3629140210823f3a34e477e6b53113a069203acc49a6df56466be294f", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000204570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a701de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000761345ca7c2d53dbb000000000000000000000000000000000000000000000008b870c2106c8c9f12000000000000000000000000000000000000000000000006cd123d6787d5f61e0000000000000000000000000000000000000000000000000001002d68347fe80000000000000000000000000000000000000000000000039762a361bdc001470000000000000000000000000000000000000000000000026a5dad53a4a4b285000000000000000000000000000000000000000000000003e80e5f348c1f0ed00000000000000000000000000000000000000000000000000002827ef770aa5a0000000000000000000000000000000000000000000000066449283a973d6b2b00000000000000000000000000000000000000000000000d1cb05c943f85c5bb000000000000000000000000000000000000000000000000816537512683c953000000000000000000000000000000000000000000000000000080fd212dc224000000000000000000000000000000000000000000000009dc3fa8cb33581c00000000000000000000000000000000000000000000000004e265d65414a2a8d400000000000000000000000000000000000000000000000276e6a83335c8384d00000000000000000000000000000000000000000000000000024b509b4c9db80e6e27c837b9a1a1923e8c9013d3cac09f0b4817c01ef05bb32ea7a2d5643f702f3951aabb1dcec7be05b260c1cc9fe78192c4e7b28117c43405ec7760fe96771bc4c52230e3f98f1875f605599256fbf0270ddcc968a64a7014c30d7398512815fe25a1f3abe0923a1bcf8c2c5ae29fc488e5e1e3e4e7a819a5c6df4e778d341ec33e7903313269d4e06e2fa197669578c4b5e7af34d3f55a925092e9f03eff0d1a07cb6fa3bd31057dc9a4612869a43111515711ab3add668ea7f12502ad29268b03d5943e65661ea0785d1e229c42caae4886f2cfd9ad84cacd2b61038718174bdf2a71139c9f14a8a8b51ce845ef31202a179d34a6e4d4c17872da4e0b410a38c90ca076a93628f45fa382487869ad8edc226607ee1252cc23c673b650432d7c4d654077b1381ab648de7304fb15bde53fcbef07e16ff7838bd271f3752703ea62c5cf9e28fb278a5b43dbdecad28747c605880e0c85812fbaecddd1e97614fbc8992745d18c6548d66a24fc658a904f1e8e19cb92f3850d3db1c5bfd16b17ec5c20ccd08e444aa78a73e645d1d770ad63034aecbbb0c71e97be1505cec7103620f83fd4a8a6134cb599cf29f3a617cee54c97baa7827a476c0b657bfc9227d6461dab75c7b60827ff18e4880878ffd9953f46133793a1ddb4df697d8a84235f36effdc76e0cb2d11910c0316528b8fa384b3685cd6f77325dfb76db2fb10a50ecddb918375b561f762788e80bec98394f93e8d584597f9e47f81b05ca4a06c3303731dad5cef199663a1da0000ffdb969f87f52c7d25b103bdef03f95c41f9d847132ff0dad90ab0ce6e4b4f9e7948ff55a50289590c99ee16fe984dd9206549ec931dfe0a74c9a19b35e8b4ae04ea55f0c3114f2381633b68a4cb888f6025002a9b63fbd8ca7b66310cd02ed8891410caa8dc88d41e8a415ea07b502ae26dfc0f23274949b49c1a108d582929abe99d5e1ec1afd6081295aa95d863aec0dd1d441c652d147aa03b7778f86cabf4feff9f0dcab62f382da038d931565550a998a6300177ef16852ec8ea7177ae7c31f03dde27bb3ac04097e5deed75536048b982ffad0224862a797fa596abcc7aeab4cfb1128b4d299ba57569c14991d104dd80d47def6ce4920f6463a127c1fec07c957660411fe77659ed9909c18f214358649fb4b7a53f9aef191c6a619b72f36767aea866ca32fec9f7c3c4c0cfc29b9270301e43192de74b22312ce0307d1e779029dc0cc2641df177d65af20a81a9d4c164c9b9fe17623299ae9a439d24fb528ddaa813e1382f50aceaeba4f4e17b2f190875ed3169e8830ee9fd4e586d28b470a436c422f78f54b5e31c22eb71ba6ea5175e90b9e811afde1984fba4ba7fd7138d4f94110bc94f61ad6fd0ac301ff8759126112a8efe97ca7a74270f012745d95744e9b21e3e1114f142e7f4506c1d5b022a326603cb9e61d7ef9ca9af9eead0688cddf1f0e9e79a7c7ff7e7216de2d52d6366c50ea4d72980af729287299062bf016585a80b793c2a618829315b8d32e593cde087c0ebd0b3e2f68b60a77b62e4b6dfac551c1fddfca9d5654086007838dde5cc67af1cd5347b839fbc2a3b601a1428a3307a7b6c452d39f5f2efc48cd200c0ebb69b4a2fe4a0301996011766ff5f9e08084773653ab3c5a6d304646851f1229bce4441a26fbc763b1ba3d202678edc29558143d7d92f080fc2f293bf4261611dbd5d71579d50848f53ddf1ce4bcfb3778b15001c6e36eb67b27c366f944a46e338bcdcc1b22e8bbaf7a9ab749a716d1548281ad670be046a1117b9bcab3cf93eccc495c360c813ecdbc940e365c5d07c9c865d7994db72464134de8862e05a5aac658d1306115136929111056b513112b94dcd6913854e4302e9b8da0d5a3688e42a77d31049afd93edbd6f663585fae0ca9cfb17c5445c392c05e0fc2ac1a9098f615753142ed714a445676cf0fd387f94b28d1401969afd2e781916a46d55152898226d4cf8c0adda617f97a24a25c751a081a3ba3a3675239ac2cf89304c1fcdf7f25faad1b93e2fe5f9beaa884667928d7c972b99806016fa9b334ab9b1814ac2e603d04fa8130f226cfd31c5b5be32bb2ce6fdf306bd0aa47f55c31ecfc90c8eb69409924a34d436da7131224d8d59e87a60090ce419035ee22b050700bcaa8cc410c28ce2ed851bb391805619eceedd821072c46d2f0a561496d041a3dd0055a92cc70faa93429e96df9ca9ef2acab8cfac2f24d5df1a26615e44b4c6d68ba0a63515bb39a44c7eebeda53667c6856dde919c0cdce1282d60e540aa535277722c12e550f5dcb7c5a569b7aedad6aa81024ef29a2afc2244e8a0baf20d2b9808e1cfeed6c19ffa1f26cba3a7556f011856729b3148c41cc576ed547450b60364898b16b6f232578b071ea368e16d02c46140ebcdddaf095cd608d557752f0446befadee81920378d694d287f2cd975cba75edaaccd0c178043322281e63cc86addef17f93adf0142b745a191acffe2df9282fa8cc59e26cd11d3c234129d6e24ac7a80edc91f713456f42feee9c5dbbe58277a99dc960c43965ccf8f5f37600c7cd4e1e7287afc8b239a5d98ddfe797b62e0aad862ac1e5c12264b95bd1d2aa1034ca1b10d6505d7d3b031e472d87162656807ba94cc1fbfb73389e39a6670759bc9104769112e69888ed9492e5f0601ef4d327362f229eddb2160bacd58c824466a0e068df115e9e7a26ffbae354855b9bd12c4f5fd164e76f2cde40af0153429b6b93b4124c8515d2a0df018c362c82baf441fbb112b59b39e1ad440480ae845da5f0da7968b22812de3d55c505a66e83185232b611e5aefca50f49eb980bd09936da2408bc3ae21fc38b88335a942a1eac46be3d02e3357d06d2fea7d7646a63ac328be633834be65d0e7677c297fe6603415c2db023d6c7d93fad6323bac47959ee88cf9d7dd7afc975b4a6ebcfecf6d94f6dc0916fcaa73a9424f5938b3a78c88de738c6bd2d074f6fbc710a27522c7d072de3b08031966d19e3e6145ed03c8e7424398806d6be0745663c1eb569795a17397db18e99c9d753fe65c1fe100e7e578ce6d81b56c8641163def557c9de707bdf2ec109ce790c623693a7ac09bd00f138b54fb2c0ef9677b1301b9e943b3809602ca0df08e5947ab7b3dc7b92a07025b945524b9b60e13e22c85383b1ab647414ab520a9506c8021abd346b28bf084638e0cd5cd4c960a303848e7fb702941bd4074112323159a17e65f591c2c51d15013b311699085d29844e713e49d7ca3811be90b445407efb3f1491a9369be66f1733cca7dfcdda44e8456619db1b35c7fbbb5147a17e0dd516d24df54e0d2f30b0d0bb367027047b55b5cc6c545719c3d201a0ba0329ea2fecd891636ed7c518ae9a80be70a3e8d68a7b6dfb5bbdfce44df05271906694bef9991dd2b93771a8c7e56e49ff2e51daf9e0d3f5c57c04ca3b9d420572b59914480369b3533395ccc1d59dc5cf936157748635d0b0df287b396401f3febe2b66640467807b3a561d60a6f33ccd715c6ce2616d460e08e8c297f2115985256bc60ba9cf52fcd0e0f0006f483cc86d3d3054546ecf7515c9e2f22db0743bb98f49cea03b2d054e07f9387c2e2c491f60dde6124e4e653ffd01758fd1e90208914091428bc223f9bfb7d47769f54a10351825944e470a34452e4383e2b9d8cd677f4272bb6f301aff517ee19d8c6d26e50302c8ff881ee3c372b6a0210b261c12f780aee28170473022a0292ff986cff82f41c1bf6e75f6564d14acd03d82c0ae50678b7833c469454295cd16fa86a3d77b7d3d1bbaa592eae84ca3d1b0b97998674080de94b61bf612cf7a52460300b2bfbd8fe0be813e990c690a81f73672cdc59302ffd1769bef0539a0d8eaa2fda0a846f80d6537a8fac17d70b1321294f90ca6087d1acd15b62ebbc2b1980662376793723cbe7f66c7996385d0ba3ef57fa10cd3183b4ebed00456555a286fc6bb8a56c6f28fd23715d5d3e641436ced825920c5e35bbae4bc1ee184d4f4e1088fd4ed3442992b23549757fd41e41a9ee00a756d4cbc5093c733847c272fabbbb943bac49b7f67b94613f8aca2384c75ca769712a060b11dbe7fb946a62488777ddef3eb4d022ffeec19b1af115f096e92084cfbcdf2a87f2e6d694f48fd6b90421cacaceef6b0f065a9803ab272d9c7a25de1017cc54f1b85b02ae8f3683b10e9b76f119de5d5e440f5692e30ab6d935403785eda862e7c458b7fea8b5c840c0626614b8a8b4067b530534fa03b238061ba8657b39b434331be2302673f0ce60294ba481d77d8e727817a50119c299ce0bda3326499d53650ff0e6c91c67d0e4acdf376993c2fda330aa4c0815cdbab2c0f39a306d810d2740ca6235b2aecd44aafe2d19af35ed2e91821d750cda6efe3750af7fc6e9489718eafcfa890e46ac80530a09548a9f868c1de68d0777958e2823f42220d0c7335bb6530ce1c9b1d0fee4eed8722c1c274b34b733167e0068c567ef19ed10074eeaaf32868a0db6339aa06f7a4775389630ca541a0ad631de05794ab4b435d7a559c9c15a70ab7b749408887c2946febfbbbe94ad0e0564652c81da25c99012864db017181b0a354eb669e8cddb80a4faeacda61202161a23dda5f03090d98f2e1ac41e1744bd54e742930626a3282801296273b704198d9fe2e77ae7f27ecfd799ab594500a7f06fd7bfc12df12dd7346ecd2cec1e179b0e3d0df234478fd57da5186662715a06215ea1cb9a51c8c7cf764ff1370fcb71c852ca352e691914f882bf7ee433aba9b9330dc788eb0cc3bc186dc4970644530cae4fe9eb4378ffc5bdeac5553003b488b3b104c54d291a6a02aa740d169a8edddb15fe361b37ecb7e6b656e89318cc4d6179f12ce5d83b462fb147fe0fa8dc0d4712694f99a0fffd3e7f5b26a022bc5a6187dee65d95fdcf53a5c9ca2f8b6e698e1e40e5ea563f87d14a1239cd00f98e211f0d4366c399b019fb0d370862d7f7123d0f3e5d7444f8307ee9f572840500405335cc5caf32186534769724315d39be30984007825f0f39aa8aef7162d5c71fd92206526c0f9281b045e30872d7863e767a51baf7528ccdc40b3b7ddc901272aaf49e8db445745e091d521dae2a349eeceb8ec2005f354d3f7baa8e241dce5810f73b52ac51a493fd577209f0bc1f8f06b64eafd62cc335ab483f1cc360f4b1fa696040ede6f2e45072be0db1f34fe0ad83c4f68e6b370d178b3e423025fa00d566ecedcfebd7eeb03e4e1bde0a6a0f2e2274a02f53f98bda1bb371a31ae52a4ed4113b33536cd0c682061d8b41c9623bf740d5c9542de5bec1cc026bcd0fe3f13c04fb30d54bea7125210a1837fb2a45570cab37d71f08016cf5c4d068cdd5dfea9633e0ac24157a428f1d1a6cebb0fecd4a9e7beff3435ac114b0c1116c1dd93b5cd09e7d4677403dad1f0744a62811431d79faad9982487c683a569cbac706d7ab92e34c95ebfe7d161a726e7458d7fc3db95918c3e10fee2f27229cfa45a72b2f784aac5379fd74282ea92b48b37c426ff65b4ccb37b22d94bfcc3533e8e31cb17a6205affb577b58201d8b85d80404b34d56a77e70c80cb71825a1a778fc2ba73eaddd095f2a4e951c1c05b28c934c28a081775b1997a68f21341fb519de93f6173dcd61a81952d22ca3454699b888d7dced53518c2685ee2b41f732207d2e55bba6a3f06200ac5809f3b8e35d9492d38b35c6cf7f85d684e43a27ba99f6f4ce0ca03503fcc40ebc113288f7db53d33d2d482071bfce317782e9645c80e799b7e8fc6316c6f967040ef9466f2819c51aab13c380c59c053e9446a1028af2ff140a45ab1d100e349b1edf43d1e608258ab24a036acbe98c5ca2cfca7b16427a0a8d92fe2bc724c2a117df8c5884b8dce4e8c72261f6b9938fbfb5eeb6f934381cc418f6bafcf8fe3e1df5ebb5478bed6764b2dcd8fc4c1637c5a4c652dd58f70e886298f034bee54520854cb53c29d5c2486d9185af7d0c4152475ed2573cc42918327229c33ccf8f24dcde531eba48b772467c065d349b529d64be896641535f5599160515f4f65d088ece267511953dbec37ab611110c0a02fcb3bfe3c5748a4c98e600a17cfc7f10d556cddc99ea9dff8534d94bb47fb8a85b914a3af2f4bfad5eb674ec20312e19a85a9306294efaac2e2fdb21820ea1f3c2559c02263cc169bb598f34d18b81142f5f2c0def1eb95325c656a88d2837206e7aa3c425fe6237b5b9ebe9f451dd1cc8d19a07a47c81be7aae09274f815990821e3c78cac3a0601017a4c15a42fe2d0b165d7512e418a00fb57e026cbde7c44f658781f7a9e9c2e7da5cd5ee7d1816e212d7bbf79c15bd799fb57016904b3875c8bde238f4920aabbb76edc82cb42b75b86a1ea2b7ee9080c5a0e97692ab61b23f07a7a7c09401c5485dda082c59169f741f1e2f44631b2d80ca48b06a5253484f3cabfb5f607c77f0826907d9d52b6e7529bd165edeea3b36818eaa0060296a398fa46ac4b136d9d5a29f8e7f60106337b94b2c90432384b20809c8a9005e1697464be3e2ab781aaba43adca93f208fa31ad74c3a86beda603e27f7b45150462c1bb6dca0c6fdf284839f9f3651183b01559bf3832552bc672425e5fe95033e8ed38eaa570a16833a49cd91935e21a1ee9762f06f4a404a16365419615996bd31c236f93c133bb8bf389d59165425f44ab020f37d31fc08ee6d5012697363a55e232c7f1d14d690cf207fbc5da309136b84a66d5e28abda47856ad76934986b0b5cc117943a650db5ecf6001c5b0dc21cd10696ece41b9a5627dce92f10f68f69ebbb67068bcfee4016d19b17a119cc81a517120663c27a93dbd67b613455e938161bf474cbe45e5b3d491b60a3175cb45c100cbb4a3163e18690166a70286371e26fdfc3bbcac5415023a3e437115cc35d9144e8a7971f22d083435bf2d49d7c8fd8d4363313041484c5b6c6d30305ae16940272795adc186de9034872b98c2efad741eb649e5912c6eb24023e2b38073d9a3a16045fcef840f36113013ecbdb4e31d382162e3373422d04598511595178e82b2f4e5072b4583cf147d64a7cdaa3b569f952fa1e055b09e567a32c5861ae96b79ddc406d8c438de191930afbc11eaf0b207954e5e517461f6fbd2d9a7250213742520756bc0455a7fae69af6365ec24e38d9d218e49d96e5dc8e2c5e5afeeac1c6599386ef05ce9ecbf1f345e8d4bcbae174e17baa4b10685f111c2c9bb4b551af806a0878c7729b1778eebe2ce15f556a14a9dbce2edace73fc1cbbcc885f665ddd48e23441a93d13db980ae95c657aae496e2b6034636385c201758d600a911420f217c44cecd2316379aef9da72d5032bf4b0f5257d13a18421fdb18fe6db460aeb07c5966715ce036a298a9cab35d9b43cad7ed5461a43a71077e028cd20efff2051f734e275204014bc23a0671ed712345f176ceb999e1125d58eed0c5eca7ebd3619708151920fc43446dba90f2f88247db17fb4b64edc1ca88d2380d3ecd9f7efef0298007d6960581855b932d35a4213928d508e77f829fdc3bda81856e981ef206847d2e8085458e91d13d6dce26d96aabda4e9f9810067f8dd7c4f06dcca9f95a2323235341616980069d11121bbcd33550279e3901b1d419ca0817dc7e807427ee29a2dc91b6c7f6e4eb4a1a762a307646f41d1d11312cefd3724e2b7156d53f69cac49d3ae19f65f68afe0aa6092d7386f1cbdae2b3ba163ee69c10ec3faa564808adfe260e8df21ef6e7f9ad57c8b805520e76b2826b35879dd321583e8cffc4131b94ba9c4d231f8f995594efb9e527cc7f9012bdaffef70dc799cbed3d4940efb82b22514a4d5698c6cfdf7b3662a1e5421092e804225c58a77d7b5680dbfcb9f9df2ade756badd51b7559b98525aa54d4c3318b3c4d5560837a3a729a31ee310bd6207631f5df9bf3e8a6218d352751bbf7a250f0c5a646fc90263ab7968d9fbb6ef483641167c8b0eba1377191e20093e2e08d6177beedf571ad42bffdcf96fb9f40501f5b84a78960b66985df6f9c057a41e9fd4da23baa7b909d5aae3538de740b2a4c8ec6584fe41e631ca83b86706530b578962823e8643199dcd9b41819644cc30135e1e818c4bce1f57de86b397a5156c7553d625f36903c845d863611600f84d78277e3d9790605e8cf2cadd71691729717335c736aa6a9153b6ef712cac2b82d5c2a7b715f768806e65ffae5aba240604ee50d572eb6ed4d07fbade6c4b32c277a4f69405043529f0beccf243b72074b14364416e5973d7e42583a68524e044afd37e9ca1aa1a29b76c990e7f5c1fd6d7575a0e41d547663d7cdfbb830cf27b2cb11fdfca499609fc911f2d3fdf1c0e6ee6ce979f7c549f2b0feffa37d663f01f1f809d2e46abb7fedf664dc0c4043ba054f3faf9e871c76e9cecc8e7434c365404b4b8bb414cab36bc9b7e24c40eeba3060ffc01be48087ee7f2fb4d843f2fb45c0b5c74a1e76fb2bd1b617d7b0d7c329cc7067c00b4da2143ca6c27a1b2b539007cc5ab3807e9515930daf5092ed74873359ed5fd89a3b4c5de7a6c9a43577925c920fb0fbee7ea3ef9e96fc426804da3e6d126bab1208bb546038d090b0132981f367100dcfb0a115c75eee7092f5a8c776c075b6351c568fcbce310d093eac86cc32dc95dedfb3662f97f0d0387c492a639c2b52fcc1cf99da62bf36b08e5bc35e9b6366a208a654b1a30640e4c335a59c9ec05d18df44d34f5ed370da8b8f0d2ab0f2374532d2b4d46e2612b3c3bae59697c2b5aea7e9b009f2b00951916aac7ca643abdc5754bf730368803257f051be59dd6d8254d72c26ed8f3a42080d025c9eb3af7cafc720d6f51f4250db73b27940954ff31120b47964a30fd00e16de908631f420758372025fbc21e38ad3f651f77e0584d079988fba4375b48de91a4d75a72a8c78b852cc847be0445783486e4cb50e5b85553d0fb63215451a9048de4f1a2087fdfcb8d07f9002abe6c018421e696859aa05c7016d974c0e0e85f2525399a0edaab9bbf8a46a407e8d1d4502b787bc23a7314259d0ace3713c2ae0a78e369c102b86f9f36d8411f5046937b2bf1f1cad431270589bacff484b44aa83c94e55c6d350ae9395cbf1e37d06d364b07c2d7f961bbd43de17ef977e7b6a229c9600b6205a1f9f249ad02a3dfde19ec25c2aa9765e94d13caecd230e580ef300c96ec44c639652f2a65169297f49c5be4633470ce866727e481ae8bf475265b1f952fd3e64e3025618e0b48da97f0ce76544cc4e612b311015134ad07bfd2f01f0e7b0a1cbf55ce13ae0032323a7954ad174d2428a1b223939427d479902a237f1ed5cbabf1efc9ed3e1d8c46fcee1f17ac272b613341201a440a26aef199517feb1aeb8ee3b4c3718f1fdb0a453a99daef7589ab06fa32e8c759638f52cd6bd729d238d3ef03d2d23d0537c2f246edca8e01c4368f885c7d098d13059dbded59987cd139baf6692e8f1f70a4f865196a6ef4f9a78710e7b5e4dc8926712b6c13b3d7c6a8b575627c5312bb48fd8ec7c9d942c79e222f04dcaf35265f3b5c8c68d5839aa30dac5776962c73669bfcbbcc715636c059f19e7b9a56fdc0eeac9243251b18400be5b8219b1d0711db43dfa2de7c8a7ae15306976fa9e54a4e48af3d2070a6128b45d0ac14217a55d88aea1a541a38291fc8768325805e31b0cd90e854eea3c08de49ae9be26f6ca50d10587edbbe5237f503a200b02e3d8bf97941a463b4c807e87df81fc2a0e3db9c7becfc264e70c61ae05782c1743e4b62aaeed19c5c5c6c36ea5a8a112ef0619b7561dd8ad7dbf428231303dccd9f849d5871a2e9c48eeed6708f21e20472a162e8c02718790d4505666091fe541f53f33420043e4246e8684c5ab03146995c0de1d706e6c447a3e4f31ad14d466c86aa237e5141759e2112e47f9db2c1930894f7ffcf965f7c5b049106df1fd68b7a78b75e8eb39875d98d9a2e3181c1c4aef24e43a1711f983880bdc0e75b227c17803c9769dcd80440761253aa22f3f9ecc853b8ee6d1a712a2996f7f8c8b13595de7726d7cede0cbc6241620f2292084c96a93a786dd4d6d068581949e42a75c6d50dbafc1832e2e53b37b6e2302e6e76dbe91f82a68d8c1af36433b6d107e330ce64f099aeb0a9278cdd19532114a11142e94c83ea7399592308a43f458611297c378b58ed93a8a2083f292ec038daea837196ddbf670f167890e789206a7776f2fd92d7cd5dbd85b0a36228209cc11a51adf334a65e1e7ccf864ea627f7d2910315ac276b476efa4dd2c17f92041e624375b9b4a6822eb10dc30c7e63b8bc16b51d5c41c608a91714284a6c6133924beeb4462b7427107acde8d959af73d9cc6ab5c7fc24e782b39410899942176786f3239ee9161003c53fef0ff27499c0fdade027ac7e86a8b855dc0efee2b0d4b51bd77959a1e0cd4846b3c9fb73c40c41c1ab9479cdc876ecce5eeed3b0088513aa8537c8877794dcf3cad741c620d8965003ae8c6f5f9023ea5d888780b08afed968167a16b4d353e9268030d73f508befad879ee4ff791dbbd92ec66124dbd712a5d154fcf6c725a5e4f2096041596e279bcb0c2769e1f1d0839533a0411f20ac41148eb634d5717392ca0affae5bc908fc38e9b22bbfce94cf010c710a3b90f7b52e1ec48e89ac290e0d93dd773e29b2968130972dc3ed95ca345c61c1b04c2f811ec823da7994dde140019fac4a3ef85df611c110c237c7fcb67022789f78bdc3a97e530d2539e7ddf0b5d1044e9aab21ef58a7c85617c54177b581183064dd7e9e49eac9fc81ec3aef666708508e1104e9f3b1e7aed4ae07c8ba5071cc1af751014c5bf9e9c30a8d3ed99cb9bbd5a50035f3f344a66b2fe15f496208c7d156dbf7f929986f94b5bd5332869c5ef65ab4bf4a2ef8e86463ec5b6b6057dc06923ea3d5bc147abc2647cfe7528d657090a2e961b6a48e64cb62e56f304f12820ad6108fca5a528c1e811a12316cb349ae4592de4de806b194b085123061b22a5e185a5832aa9ea54256f537a6aa2883381022ef32699d20159d9f53d22eb7f86fe46d6ba98480bf41a9f7c0b9e5b371e85970f288ef55206ac8eae2216f7099290a045298e4330d7d1a8ff1f47383f71cc2361e5f51a4dc25fff951e1a7760e0062f6dd4e989334ae081c07ce1e0b74d8a7a7436186f28ea99a070b819ec1d2076c9d799f625e69973abcf2f628bf1e7acb8c59f812ff826acb107f2289471e7b908827a3c6731495688bb6b92d68e3b3816466b65694f5befe7ce71284a1aa2aee0cc81bf1d8045829d978bde2e6a828a02e262194f6549b3e5b6bf0f61110c827c620ddaf9bc971d8afa0be1f15af22ff3f22352802850fad58b9d29092a5d945bd68c7398c080caad8280bef41034d84dedf9bdbf7cf0de38526c186b3f9a5fd07ce6356d2baf2bf1b9635879e61cb9ccbf3775c97c28f8148a0f251b28fab4fc402761be25433aad18b73c35b839472d173da86506b4c2e60f7e1b7b4911cc03f7a161764cb25ca58b238a13afb49672bc996f240aa95486472f0e8455e8d028bb6a39d7f021f2c8e4bb38fced4d7bee1afcf8dcb8fa321499ba16671b76dea745ebb1a759ac0421c243fefc78c6e312d1d70588b0ef05e138782873323ed10ab8d5ca109f0bcd8a7e6cc1dd41a0ef22938eea18a0884a75916f2411e8552727fd0d669defd942bf948f7f78fda74ad3ab014bc21e987fd89c0f02701ce9cbbbece457abcbf25d284ae27ec3a378923e182d4d175cb923ced7b50f2302552a944503674e0fa87e28f45070e275d5320c2f3d23bdfd5ad9048fba083aa2e42c7b24549d4d36e2fe8741a1e6528c5ed4e75cf2a170914c181949d614d64c25267b569c992022b998280b1e5ee72c0779578eb2da5b8b485b3d406c1f0a02c9de1896e85ac5b10fc9e878cf42d86e9607c93fafeb66f6135bcb18332947238aa807a50b9afe7423ecdb65ab85f05494862b572c835ae1f5989315f70174c2fde60f5f69b5a3ba28044d3f719737b29b5c4c479aea59b55fdba84399167e1264a2ca15d68b7054fcdae548de6d141239e96ff593aeff299171346b7b2f8a746a57b471a0b5468100825870533d1ff5690c3a35917b009d7acb0b910f0eb080bf486bb09661378c9af42d8ed3b541ce12c3eab09390347db7637f4e00010facebdb3973ca5cb2c2b1eab263e8c934886c7bcb041e9b3f6d1473b7fb2d03612e8108986804004366d8c81de6ae5e3f2ab2a5e875cf2d9fb1fd0aa60b491cf46d69f552ceb8bc3edd44f7782665af728ed70248f4d7a47f31c661a4aec0184f40de72482ed8294defbb6f0e27f03e7cf2717137f7747c5a985a6d97758d1b5615444c4ac911bff57a15164c8e1411ef3baf99c3cc00a525e6bfd878302f2362fc38e16b7eb6d7b1081b7321cceaea15dabe7af98dceddd4db928239c90123fb16abfca7b9dbd319fcf54a828bd1f89a4edff60fa3d027ddea22f6b8cc082bd0588ccb42c5bdfe0e2de6105c92eb41dfa53ea3e644fa43c66710233429272f04be3d84767cac1a318739ac4771cfa1b8c7857bf0f302806b94f7bcd6a0a02c2c7b9ee9f7ad1a4ad3edf98f7edac4fa84022b5a0cadccbb598a7828c600fb055721c86fdfc4f1836cc81614fa2faa8e3b7658912e860e7ab1eb9e1d79572710b501d8ec29001fe59e91c398b3351e93b68ed300a9513bb9e76fe582388a010d82c42dc111fcfb6b428985c6add8c22b8e2eaa12d83b8b06ed74e4a96a908f251cd86488618b0adb1e40a01835af12200706112bd58da81d1a2012eb2054632b6684dbe5e31054b578775c49bf3820cb022513d611675c940008445fed94f61754bd324aa37a484a0f4f51be92d44d94e6bd47dd1f29452f0149ae1164c57d0cd4e189d2a995cbf93f371bab53232cbc8d99caba2bd108e869b66191089ccc09f74c712e337c9959eedd69584ffcc90bdc11f6d0aee4c8a96b03921c78a8bb1f78d7f70fd72a759ae09c54f6234f86a75d6de285f1b07370bfb66d1ffa97602999b9eb40b834ea307430ea7fb49497a83a375d6006c2a3c5be21b4393563d5268c99d7e8b70e036909f8596cb636f2bc05a86367ed39ac198d2ffb190259ca2b17c86c57831633f304d7216f80b2664f35e3276e76f39b1739c8993c0ec28b119c87e837d61a1e4ebc7095a3a3d522e969276959de691df84e1553b9d5f7b827135b07eedb11acead94ed5cda852fed50bd963686d42a9c31498b2fbcc804729b08b24aabcdc54028d958c66451e1d289c47043a8603908c57f117cac8f27f121368624fbcf0546e6238d7248a29e0a3b5ff2c6ff182871a9e7b7585a4c04e13253cec272cc65d5bf2b4c864bdb43bba0b83b1ea4eff53b5f1f449a9f85d7f27e64532d38ba28e829852e8fdd4cddcd8ddbf5a9c0c1c738a17b5600cf7070c22d99d8b95d617d444871d91c26f6047a1e9f98d590e193c8bb16887c3f987a10df34a36ccd6de19633479b2d7a0c74b080ba140bd477c2f0e88348c9b4497da2d464ff71dab0d3816ca8666644550bac74327cabcf9c762a7f934705957b13b2b42d06efa1d7aea4ff8d73b62a5c2fe26f1bded97a6d259d7cae5a4aefba06e1e19a258408a34ebb634bb89d79cfd227c490c31c4137b23721a14a085eb33e82061f8d36c1411fb2b9e94983dece932fa45f21ce3fef99b90306506f3b0efe22fe59c183c1e3fb5b2b48ec745d88808813c771a0e2779d128b0c4e80975e56c0e40e42deb6d5d026c15f48c5ac93ee8f353d966989fa1147c9550b08b0d971b2296b11070bca184bfbdf1c728d7d24dbc018eafaf1ba85a597a3affffe8c62f29aff586eaa75743a836430b57d7d81f08b3d8bfaa441289af8262f7d7f0771928f4261d643c71d5060ce9ded17000cb5f6f0e4d5c7be470226b0ddf5931e81905ad5ff8bde94bb880ef047a90262492748d4e2059a2611b2dd2465a6559843e0ae25f9a29a9aa4999fa291a1ed4c080f552f55156a3204b3c8189bdda58e8271f3ff9b466a525ee596de8881bd5a927e341bc2cea65de5ecbc44e7a8ff967fe04618944b73761bc9ca9e7c92c96b3f6d4d6afd92a952390f5c2bb853b5cec5910cd646288a39dc3132a06de5d58477c752c9dd55c522f42885e9031f44033d30865cb14c0acd736ac88b1e1a2578f9b2da6e44a1a5112a3fe90a68a9360d3420cffe8acaf2cce9c167da8925b76db9ed11ee21f2a59dfc20a24856a80ff40ac0366ceece8d72a9d021d91b00b8047ebfd7c1c457058e23088060e3d0e83a6661f14f572fab9520b079fb7263b3e944733ed7069263ffc8803fda4988ebb26a62d47654257ff30bd5ead9d2b90e3ef340d7179cbbd312a1ce6a3db19e84c84082794b0c26d58a78dad48bb4ee28fb566a91bc9bae147df3540d294f499256db40bd79c6f3ebd88d0f328e266214e96a37ccd37eadc62bbd70388417764f04ff32f21f80b5f6989fe6346bf964198320ecf03683eb51cc190d1b99774e3d9d2b20f04827a91aa0c1c1f16d8d390e13f85cb7f2207b57961eb42942227da99f52f02a3919711cdde39d33ad27d5e4bd2e9bb3c954d8591ba210e8074386c79d64e0518fc2dcfb254e4f0477b0f28b44c88fd4367b5d5cc130803b13056cb1f5859", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000e40957462902168235249211dd6ba6ee00000000000000000000000000000000f8defc3ae63e7ea8b9be28634acc72d901cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002069abb3ab89cdd02776ca33f3ca6e0d10986b7d8cb986d0f7483b530eb616eaf1ff0f767423d4fb0ac0d17aeed5ea083d9f2bcc677df5fffd068a9470bb0650a003f9b2ed5d6ab5749b0ece4b4aa05407fa4ad7f08b38aed610a3106e903bd182a2e15e0c5d25a298fc02ff12751572e031a6b9c5317a80e14909d881df864a5000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } From 4c0d5614b500c7851bfb7d8c2318faf07acaa704 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 20:58:38 +0200 Subject: [PATCH 36/54] pin canonical preset to insecure for verifiers --- circuits/benchmarks/README.md | 12 +- .../scripts/extract_crisp_verify_gas.sh | 15 +- .../scripts/replay_folded_verify_gas.sh | 8 +- docs/pages/cryptography.mdx | 7 +- docs/pages/internals/dkg.mdx | 36 +++ scripts/README.md | 78 ++++- scripts/generate-verifiers.ts | 268 +++++++++++++++--- tests/integration/lib/prebuild.sh | 7 +- 8 files changed, 378 insertions(+), 53 deletions(-) diff --git a/circuits/benchmarks/README.md b/circuits/benchmarks/README.md index 6531de85c..d5d65b41c 100644 --- a/circuits/benchmarks/README.md +++ b/circuits/benchmarks/README.md @@ -181,8 +181,16 @@ EVM verifier `estimateGas` in `packages/enclave-contracts/scripts/benchmarkGasFr `ensure_circuit_preset_built.sh`, which runs `pnpm build:circuits --skip-if-built --no-clean --no-clean-targets` by default (skips recompile when `dist/circuits//.build-stamp.json` and marker artifacts match the current circuit sources). -Then `pnpm generate:verifiers --no-compile` refreshes Honk contracts before integration export and -Hardhat replay. +Then `pnpm generate:verifiers --check --no-compile` **verifies** the committed Honk Solidity +verifiers (`DkgAggregatorVerifier.sol`, `DecryptionAggregatorVerifier.sol`) match the current +circuits' recursive VKs. Benchmarks do **not** rewrite the committed verifiers — drift fails the run +loudly. The committed verifiers are pinned to the **`insecure-512`** preset, so `--check` only runs +after `dist/circuits/insecure-512/.build-stamp.json` confirms that preset was last built; the secure +benchmark path (`--mode secure --build secure-8192`) builds and proves against `secure-8192` +artifacts but does not regenerate or check the committed `.sol` files — those are locked to +`insecure-512` (see [`scripts/README.md`](../../scripts/README.md#verifier-generator)). If you see +`❌ Solidity verifier(s) drift from current circuit VKs` or +`❌ Canonical preset 'insecure-512' is not built`, follow the fix recipe printed by the script. - **`--force-build`** on extract/replay/ensure: full rebuild (same as a fresh `build:circuits`). - **`--skip-build`** on extract/replay: skip circuit build and Honk generation (only re-run diff --git a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh index 717e85a41..9b1291870 100755 --- a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh +++ b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh @@ -118,20 +118,25 @@ else "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" echo " [gas] Build artifacts ready." - echo " [gas] Regenerating Honk Solidity verifiers (dkg_aggregator, decryption_aggregator)..." + # `--check`: verify the committed Honk Solidity verifiers + # (DkgAggregatorVerifier.sol, DecryptionAggregatorVerifier.sol) match the + # current circuits' recursive VKs. Fails loudly on drift; benchmarks must + # not silently rewrite committed contracts. If this errors, run + # `pnpm generate:verifiers --write` and commit the diff before benchmarking. + echo " [gas] Checking Honk Solidity verifiers are in sync with circuit VKs..." if [ "$VERBOSE" = true ]; then - echo " [gas] [verbose] Running: pnpm generate:verifiers --no-compile" + echo " [gas] [verbose] Running: pnpm generate:verifiers --check --no-compile" ( cd "$REPO_ROOT" && \ - pnpm generate:verifiers --no-compile + pnpm generate:verifiers --check --no-compile ) else ( cd "$REPO_ROOT" && \ - pnpm generate:verifiers --no-compile >/dev/null + pnpm generate:verifiers --check --no-compile >/dev/null ) fi - echo " [gas] Honk verifiers ready." + echo " [gas] Honk verifiers in sync." require_preset_artifacts fi diff --git a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh index 3ba027434..d3bfc4b05 100755 --- a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh +++ b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh @@ -87,8 +87,12 @@ if [ -n "$BUILD_PRESET" ]; then ENSURE_ARGS+=(--force-build) fi "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" - echo " [replay-gas] Regenerating Honk Solidity verifiers (pnpm generate:verifiers --no-compile)..." - (cd "$REPO_ROOT" && pnpm generate:verifiers --no-compile) + # `--check`: verify the committed Honk Solidity verifiers match the + # current circuits' recursive VKs. Fails loudly on drift; replays must + # not silently rewrite committed contracts. If this errors, run + # `pnpm generate:verifiers --write` and commit the diff. + echo " [replay-gas] Checking Honk Solidity verifiers (pnpm generate:verifiers --check --no-compile)..." + (cd "$REPO_ROOT" && pnpm generate:verifiers --check --no-compile) fi fi diff --git a/docs/pages/cryptography.mdx b/docs/pages/cryptography.mdx index d4214eb78..2d4c6bd3c 100644 --- a/docs/pages/cryptography.mdx +++ b/docs/pages/cryptography.mdx @@ -508,7 +508,12 @@ meaningful inside a proof. Some statements, especially the large **C2** pipeline, do not fit in one proof: the repository uses inner UltraHonk proofs, wrapper circuits, and the folding machinery under [`recursive_aggregation/`](https://github.com/gnosisguild/enclave/tree/main/circuits/bin/recursive_aggregation); -see [Noir Circuits](./noir-circuits) for how to build and verify them. +see [Noir Circuits](./noir-circuits) for how to build and verify them. The on-chain Honk verifier +contracts that consume the outer `dkg_aggregator` and `decryption_aggregator` proofs are generated +from the circuits' recursive VKs and committed to git; the repo enforces they stay in lockstep via +`pnpm generate:verifiers --check` (run automatically by integration tests and benchmarks). See +[`internals/dkg#keeping-the-on-chain-honk-verifiers-in-sync`](./internals/dkg#keeping-the-on-chain-honk-verifiers-in-sync) +for the contract. ## Where to go next diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index 5bd43d2c0..6abc4db7b 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -410,6 +410,42 @@ read to identify which registered operator produced which DKG output. The mappin (via committee hash), the attestation binds to the proof commitments and the operator key, and the operator key was registered against `topNodes` at committee finalisation. +### Keeping the on-chain Honk verifiers in sync + +The two on-chain Honk verifiers consumed by `publishCommittee` and the decryption flow +(`DkgAggregatorVerifier.sol`, `DecryptionAggregatorVerifier.sol`, both under +`packages/enclave-contracts/contracts/verifiers/bfv/honk/`) are **generated** from the recursive VKs +of `recursive_aggregation/dkg_aggregator` and `recursive_aggregation/decryption_aggregator` via +`bb write_solidity_verifier`. They are committed to git so production deploys are reproducible, but +they must stay in lockstep with the circuits' recursive VKs — a verifier whose baked-in VK doesn't +match the circuit will silently reject every proof. + +**Preset is pinned.** The committed `.sol` files correspond to exactly one BFV preset: +**`insecure-512`** (the dev / CI / benchmark default). The recursive VKs are preset-dependent — +different BFV parameter sets compile to different VKs and therefore different `.sol` bytes — so "the +committed verifier" is only well-defined for one preset. `pnpm generate:verifiers` refuses to run +(in both `--check` and `--write` modes) unless `dist/circuits/insecure-512/.build-stamp.json` exists +and reports that preset. If `pnpm build:circuits --preset secure-8192` was the last build, the +generator surfaces this loudly with a fix recipe instead of silently producing wrong VK bytes. +Production deploys on a non-canonical preset must regenerate locally for that deploy. + +The repo enforces sync with two modes of `pnpm generate:verifiers`: + +- **`--check`** — used by `tests/integration/lib/prebuild.sh` (when + `PROOF_AGGREGATION_ENABLED=true`) and by the benchmark gas-extraction scripts. Regenerates each + verifier in memory, runs the same `prettier-plugin-solidity` formatting the committed files use, + and diffs against the committed `.sol`. Any drift fails the run with a clear fix recipe instead of + silently rewriting committed contracts mid-test. +- **`--write`** (the default for manual invocation) — overwrites the committed files. Run this when + you intentionally bump the canonical-preset circuits or the Noir/bb toolchain, then commit the + diff. + +The version pinning lives in `crates/zk-prover/versions.json` (consumed by `enclave noir setup` and +by the integration test prebuild). If `--check` fails after a clean +`pnpm build:circuits --preset insecure-512`, your local toolchain is out of sync with that pin. See +[`scripts/README.md`](https://github.com/gnosisguild/enclave/blob/main/scripts/README.md#verifier-generator) +for the full contract. + ### Decryption recursive proof The decryption recursive proof (`BfvDecryptionVerifier`) follows the same shape: a recursive Honk diff --git a/scripts/README.md b/scripts/README.md index 68bd8debf..f94c39118 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -243,12 +243,41 @@ Circuits are built locally and stored in a git branch: ## Verifier Generator -`generate-verifiers.ts` - Generates Solidity verifier contracts from compiled Noir circuits. +`generate-verifiers.ts` - Generates (or verifies) Solidity Honk verifier contracts from compiled +Noir circuits. + +The generated `.sol` files under `packages/enclave-contracts/contracts/verifiers/bfv/honk/` are +**committed to git** and correspond to **exactly one BFV preset**: `insecure-512` (the development / +CI / benchmark default). The Honk verifiers bake in the recursive VKs of `dkg_aggregator` / +`decryption_aggregator`, which are preset-dependent — different BFV parameter sets compile to +different VKs and therefore different `.sol` bytes. The committed files only match `insecure-512`. + +The generator enforces this: both `--check` and `--write` refuse to run unless +`dist/circuits/insecure-512/.build-stamp.json` exists and reports `"preset": "insecure-512"`. The +stamp is written by [`pnpm build:circuits --preset `](#circuit-builder) and is the only +on-disk record of which preset built `circuits/bin/`. If a different preset was last built (or +none), the generator refuses with a clear fix recipe instead of silently producing the wrong `.sol`. +If you need verifiers for a different preset (e.g. a production deploy on `secure-8192`), generate +them locally for that deploy — do **not** commit the result over the canonical files. + +The script has two modes: + +- **`--check` (used by test/benchmark/CI flows)** — regenerate in memory and diff against the + committed files. Exits non-zero on drift without touching the working tree. This is how + `tests/integration/lib/prebuild.sh`, `circuits/benchmarks/scripts/extract_crisp_verify_gas.sh`, + and `circuits/benchmarks/scripts/replay_folded_verify_gas.sh` invoke the script — so accidental + drift between committed verifiers and current circuit VKs surfaces as a failure rather than a + silent rewrite mid-test. +- **`--write` (default for manual runs)** — regenerate and overwrite the committed files. Use this + when you intentionally bump the canonical-preset circuits or the Noir/bb toolchain. ### Usage ```bash -# Generate verifiers for all circuits +# Verify committed verifiers match current circuit VKs (CI/tests use this) +pnpm generate:verifiers --check + +# Regenerate (default; equivalent to --write) pnpm generate:verifiers # Generate only for specific group @@ -259,7 +288,7 @@ pnpm generate:verifiers --group threshold pnpm generate:verifiers --circuit pk pnpm generate:verifiers --circuit pk --circuit fold -# Clean existing verifiers before generating +# Clean existing verifier directory first (write mode only) pnpm generate:verifiers --clean # Preview what would be generated @@ -281,16 +310,55 @@ Automates the full pipeline from Noir circuits to on-chain Solidity verifiers: - Renames contract from `HonkVerifier` to descriptive name (e.g., `DkgAggregatorVerifier`, `DecryptionAggregatorVerifier`) - Replaces Apache-2.0 license header with LGPL-3.0-only -6. **Outputs** to `packages/enclave-contracts/contracts/verifiers/bfv/honk/` + - Runs `prettier-plugin-solidity` so on-disk format matches the rest of the repo (and so + `--check` doesn't trip on whitespace differences vs. raw `bb` output) +6. **Outputs / verifies** at `packages/enclave-contracts/contracts/verifiers/bfv/honk/`: + - In `--write` mode: overwrites the committed `.sol` files. + - In `--check` mode: diffs the freshly generated content against the committed `.sol` and exits + non-zero on any drift, printing the offending files and a fix recipe. + +### When `--check` (or `--write`) fails + +There are two distinct failure modes — the error output tells you which one: + +**1. Canonical preset not built** — the generator refuses up front because +`dist/circuits/insecure-512/.build-stamp.json` is missing or reports a different preset. The +committed verifiers are pinned to `insecure-512`; nothing under `circuits/bin/` is trusted unless +the build stamp confirms the canonical preset was last built. + +To fix: + +```bash +pnpm build:circuits --preset insecure-512 +# then retry the original command +``` + +**2. Drift between committed verifiers and current circuit VKs** — the canonical preset is built but +the bytes don't match. Typical causes: + +- You ran `pnpm build:circuits` against a different Noir/bb version than the one that produced the + committed verifiers (see `crates/zk-prover/versions.json` for the pinned versions). +- A circuit was changed without regenerating the committed Solidity files. + +To fix: + +1. Verify your `nargo` / `bb` versions match `crates/zk-prover/versions.json`. +2. Run `pnpm build:circuits --preset insecure-512`. +3. Run `pnpm generate:verifiers --write`. +4. Commit the resulting diff under `packages/enclave-contracts/contracts/verifiers/bfv/honk/`. ### Options The `generate:verifiers` script in package.json passes `--circuits` with the on-chain used list. +- `--check` - Verify committed verifiers match current VKs (no writes). Exits non-zero on drift. +- `--write` - Write/overwrite committed verifiers. Default when neither `--check` nor `--write` is + passed. - `--circuits ` - Circuit names, comma-separated. Omit to generate all. - `--group ` - Circuit groups (comma-separated: dkg,threshold,recursive_aggregation) -- `--clean` - Remove existing verifier directory before generating +- `--clean` - Remove existing verifier directory before generating (write mode only) - `--no-compile` - Don't compile circuits automatically (fail if not already compiled) +- `--no-clean-targets` - Don't delete nargo target dirs before generating verifiers - `--dry-run` - Show what would be generated without doing anything - `-h, --help` - Show help message diff --git a/scripts/generate-verifiers.ts b/scripts/generate-verifiers.ts index 88a556869..937fe4483 100644 --- a/scripts/generate-verifiers.ts +++ b/scripts/generate-verifiers.ts @@ -6,7 +6,19 @@ // or FITNESS FOR A PARTICULAR PURPOSE. /** - * Generate Solidity verifier contracts from compiled Noir circuits. + * Generate (or verify) Solidity verifier contracts from compiled Noir circuits. + * + * The Honk Solidity verifiers are committed to git. To keep the committed + * files in sync with the recursive VKs, this script has two modes: + * + * - `--check` (default for test/benchmark flows): regenerate in memory and + * diff against the committed files. Exits non-zero on drift without + * touching the working tree. Used by `prebuild.sh`, `extract_crisp_verify_gas.sh`, + * `replay_folded_verify_gas.sh` so accidental drift fails loudly instead + * of silently rewriting committed contracts mid-test. + * + * - `--write` (default for manual runs): regenerate and overwrite the + * committed `.sol` files. Use this when you intentionally bump circuits. * * Prerequisites: * - `nargo` and `bb` (Barretenberg CLI) must be installed and in PATH @@ -14,11 +26,12 @@ * will compile them automatically. * * Usage: - * pnpm generate:verifiers # All circuits (or --circuits from package.json) - * pnpm generate:verifiers --circuits pk,fold # Specific circuits - * pnpm generate:verifiers --clean # Remove existing verifiers first - * pnpm generate:verifiers --dry-run # Show what would be generated - * pnpm generate:verifiers --no-compile # Use artifacts from build:circuits (skips target cleanup) + * pnpm generate:verifiers # Write all circuits (default) + * pnpm generate:verifiers --check # Verify committed verifiers match VKs (no writes) + * pnpm generate:verifiers --circuits pk,fold # Specific circuits + * pnpm generate:verifiers --clean # Remove existing verifiers first (write mode only) + * pnpm generate:verifiers --dry-run # Show what would be generated + * pnpm generate:verifiers --no-compile # Use artifacts from build:circuits (skips target cleanup) */ import { execSync } from 'child_process' @@ -37,6 +50,28 @@ const LICENSE_HEADER = `// SPDX-License-Identifier: LGPL-3.0-only // or FITNESS FOR A PARTICULAR PURPOSE. ` +/** + * Canonical BFV preset for the committed Honk Solidity verifiers. + * + * The on-chain `DkgAggregatorVerifier.sol` / `DecryptionAggregatorVerifier.sol` bake in the + * recursive VKs of `dkg_aggregator` / `decryption_aggregator`, which are + * **preset-dependent**: different BFV parameter sets compile to different VKs and therefore + * different `.sol` bytes. Exactly one preset can be "the committed one"; we pin `insecure-512` + * because that is the development/CI/benchmark default and the preset every committed verifier + * corresponds to. + * + * Both `--check` and `--write` refuse to run unless + * `dist/circuits//.build-stamp.json` exists and its `preset` field matches. + * This prevents silently producing/checking against the wrong preset's VKs — e.g. after + * `pnpm build:circuits --preset secure-8192`, where `circuits/bin/` holds secure artifacts that + * would generate different `.sol` bytes. + * + * If you need verifiers for a different preset (e.g. a production deploy on `secure-8192`), + * rebuild that preset locally and run the generator there; do **not** commit the result over + * the canonical files. + */ +const CANONICAL_PRESET = 'insecure-512' + interface CircuitInfo { name: string group: CircuitGroup @@ -51,6 +86,13 @@ interface GenerateOptions { dryRun?: boolean compile?: boolean // compile circuits before generating verifiers noCleanTargets?: boolean // skip deleting nargo target dirs before generation + /** + * Check mode: generate verifiers into memory and diff against the committed + * files. Exit non-zero on any drift. No writes to the working tree. + * Used by test/benchmark/CI flows that must not silently mutate committed + * verifier contracts. + */ + check?: boolean } // --------------------------------------------------------------------------- @@ -76,11 +118,21 @@ class VerifierGenerator { } async generate(): Promise { - console.log('🔮 Generating Solidity verifiers from Noir circuits...\n') + const mode = this.options.check ? 'Checking' : 'Generating' + console.log(`🔮 ${mode} Solidity verifiers from Noir circuits...\n`) this.checkTool('nargo --version', 'nargo') this.checkTool('bb --version', 'bb') + // Refuse to run unless `circuits/bin/` was last built for the canonical preset. + // This is the only on-disk witness of "which preset's VKs live under circuits/bin" + // (cf. `scripts/build-circuits.ts:writePresetStamp`). Skip the gate in --dry-run mode + // (no artifact reads will happen) and in --no-compile mode only if explicitly allowed + // by a future opt-out — today, even --no-compile must run against the canonical preset. + if (!this.options.dryRun) { + this.assertCanonicalPresetBuilt() + } + const circuits = this.discoverCircuits() if (circuits.length === 0) { console.log(' ⚠️ No circuits found') @@ -100,12 +152,20 @@ class VerifierGenerator { this.cleanTargetDirs(circuits) } - // Prepare output directory - if (this.options.clean && existsSync(this.verifierDir)) { - rmSync(this.verifierDir, { recursive: true }) - console.log(' 🗑️ Cleaned existing verifier directory') + if (this.options.check && this.options.clean) { + throw new Error('--check and --clean are mutually exclusive (check must not mutate the working tree)') + } + + // In write mode, prepare output directory. + if (!this.options.check) { + if (this.options.clean && existsSync(this.verifierDir)) { + rmSync(this.verifierDir, { recursive: true }) + console.log(' 🗑️ Cleaned existing verifier directory') + } + mkdirSync(this.verifierDir, { recursive: true }) + } else if (!existsSync(this.verifierDir)) { + throw new Error(`--check requires the committed verifier directory to exist: ${this.verifierDir}`) } - mkdirSync(this.verifierDir, { recursive: true }) // Pre-flight: two circuits with the same leaf name would silently overwrite each other's .sol. const seen = new Map() @@ -122,24 +182,63 @@ class VerifierGenerator { seen.set(contractFile, `${circuit.group}/${circuit.name}`) } - const generated: string[] = [] + const processed: string[] = [] + const drift: { circuit: string; file: string; reason: string }[] = [] const errors: string[] = [] for (const circuit of circuits) { try { - const solFile = this.generateVerifier(circuit) - generated.push(solFile) + const result = this.generateVerifier(circuit) + processed.push(result.outputPath) + if (this.options.check) { + if (!existsSync(result.outputPath)) { + drift.push({ + circuit: `${circuit.group}/${circuit.name}`, + file: result.outputPath, + reason: 'committed file is missing', + }) + } else { + const committed = readFileSync(result.outputPath, 'utf-8') + if (committed !== result.content) { + drift.push({ + circuit: `${circuit.group}/${circuit.name}`, + file: result.outputPath, + reason: 'content differs from committed file', + }) + } + } + } } catch (error: any) { errors.push(`${circuit.group}/${circuit.name}: ${error.message}`) console.error(` ✗ ${circuit.group}/${circuit.name}: ${error.message}`) } } - console.log(`\n✅ Generated ${generated.length} Solidity verifier(s) in:`) - console.log(` ${this.verifierDir}\n`) - - for (const f of generated) { - console.log(` • ${basename(f)}`) + if (this.options.check) { + if (drift.length > 0) { + console.error(`\n❌ ${drift.length} Solidity verifier(s) drift from current circuit VKs:`) + for (const d of drift) { + console.error(` • ${d.circuit} (${basename(d.file)}): ${d.reason}`) + } + console.error( + `\n The committed Honk verifiers under contracts/verifiers/bfv/honk are out of sync\n` + + ` with the circuits' recursive VKs. This usually means:\n` + + ` - You ran 'pnpm build:circuits' against a different Noir/bb version, or\n` + + ` - A circuit / VK changed without regenerating the committed Solidity files.\n` + + `\n To fix:\n` + + ` 1. Verify your Noir/bb versions match (see crates/zk-prover/versions.json).\n` + + ` 2. Run 'pnpm build:circuits --preset insecure-512' (or the relevant preset).\n` + + ` 3. Run 'pnpm generate:verifiers --write' (or omit --check) to refresh the\n` + + ` committed .sol files, then commit the diff.\n`, + ) + process.exit(1) + } + console.log(`\n✅ Checked ${processed.length} Solidity verifier(s) — all in sync with current VKs.\n`) + for (const f of processed) console.log(` • ${basename(f)}`) + } else { + console.log(`\n✅ Generated ${processed.length} Solidity verifier(s) in:`) + console.log(` ${this.verifierDir}\n`) + for (const f of processed) console.log(` • ${basename(f)}`) } if (errors.length > 0) { @@ -192,7 +291,7 @@ class VerifierGenerator { // Generation pipeline (compile → write_vk → write_solidity_verifier) // ------------------------------------------------------------------------- - private generateVerifier(circuit: CircuitInfo): string { + private generateVerifier(circuit: CircuitInfo): { content: string; outputPath: string } { const { name, group, packageName } = circuit // 1. Compile if needed @@ -210,7 +309,7 @@ class VerifierGenerator { throw new Error('bb write_solidity_verifier did not produce output') } - // 4. Post-process: rename contract, add license header, copy to output + // 4. Post-process: rename contract, add license header const contractName = this.toContractName(name) const outputFileName = `${contractName}.sol` const outputPath = join(this.verifierDir, outputFileName) @@ -223,13 +322,40 @@ class VerifierGenerator { // Replace license header – bb produces Apache-2.0 by default solidity = solidity.replace(/\/\/\s*SPDX-License-Identifier:[^\n]*\n(\/\/[^\n]*\n)*/, LICENSE_HEADER) - writeFileSync(outputPath, solidity) - - // Clean up intermediate file + // Clean up intermediate file (always — we don't keep the bb temp output around) rmSync(rawSolPath, { force: true }) - console.log(` ✓ ${group}/${name} → ${outputFileName}`) - return outputPath + // Normalize with prettier so the on-disk format matches what the rest of + // the repo's `pnpm prettier:write` produces. Without this, --check would + // always fail because bb emits a different whitespace style than + // prettier-plugin-solidity. + solidity = this.formatSolidity(solidity, outputPath) + + // In check mode, do not touch the committed file. + if (!this.options.check) { + writeFileSync(outputPath, solidity) + console.log(` ✓ ${group}/${name} → ${outputFileName}`) + } else { + console.log(` • ${group}/${name} → ${outputFileName} (checking)`) + } + + return { content: solidity, outputPath } + } + + /** + * Format Solidity through prettier-plugin-solidity so output matches the + * project's standard formatting. Run from `packages/enclave-contracts` so + * prettier picks up the local `.prettierrc` and plugin resolution. + */ + private formatSolidity(content: string, outputPath: string): string { + const contractsDir = join(this.rootDir, 'packages', 'enclave-contracts') + // Use prettier --stdin-filepath so plugin selection is by extension. + const result = execSync(`pnpm exec prettier --stdin-filepath "${outputPath}"`, { + cwd: contractsDir, + input: content, + stdio: ['pipe', 'pipe', 'pipe'], + }) + return result.toString('utf-8') } /** @@ -358,6 +484,54 @@ class VerifierGenerator { throw new Error(`${name} is not installed or not in PATH`) } } + + /** + * Refuse to run unless `circuits/bin/` was last built for `CANONICAL_PRESET`. + * + * The committed Honk Solidity verifiers correspond to one preset + * (`CANONICAL_PRESET`). The only on-disk record of "which preset built + * `circuits/bin/`" is the build stamp written by + * `scripts/build-circuits.ts:writePresetStamp` at + * `dist/circuits//.build-stamp.json`. If the stamp for `CANONICAL_PRESET` + * is missing, the developer either never built it, or last built a different + * preset — either way, generating/checking against `circuits/bin/` would use + * the wrong VKs. Surface that loudly with a fix recipe instead of silently + * producing wrong verifier bytes. + */ + private assertCanonicalPresetBuilt(): void { + const stampPath = join(this.rootDir, 'dist', 'circuits', CANONICAL_PRESET, '.build-stamp.json') + if (!existsSync(stampPath)) { + throw new Error( + `Canonical preset '${CANONICAL_PRESET}' is not built (missing ${stampPath}).\n` + + ` The committed Solidity Honk verifiers under\n` + + ` packages/enclave-contracts/contracts/verifiers/bfv/honk/ bake in the recursive VKs\n` + + ` of the '${CANONICAL_PRESET}' BFV preset. Generating/checking against any other preset\n` + + ` would produce different '.sol' bytes and is rejected by design.\n` + + `\n` + + ` To fix, run from the repo root:\n` + + ` pnpm build:circuits --preset ${CANONICAL_PRESET}\n` + + ` then retry. If you intentionally want verifiers for a different preset (e.g. a\n` + + ` production deploy on 'secure-8192'), generate them locally for that deploy — do\n` + + ` NOT commit the result over the canonical files.`, + ) + } + let stamp: { preset?: string } = {} + try { + stamp = JSON.parse(readFileSync(stampPath, 'utf-8')) + } catch (err: any) { + throw new Error(`Failed to parse ${stampPath}: ${err.message}`) + } + if (stamp.preset !== CANONICAL_PRESET) { + throw new Error( + `Build stamp at ${stampPath} reports preset '${stamp.preset ?? '(missing)'}', expected '${CANONICAL_PRESET}'.\n` + + ` The committed Solidity Honk verifiers correspond to '${CANONICAL_PRESET}' only.\n` + + ` Run:\n` + + ` pnpm build:circuits --preset ${CANONICAL_PRESET}\n` + + ` then retry.`, + ) + } + console.log(` ✓ Canonical preset '${CANONICAL_PRESET}' build stamp present.\n`) + } } // --------------------------------------------------------------------------- @@ -382,6 +556,10 @@ async function main() { options.noCleanTargets = true } else if (arg === '--no-clean-targets') { options.noCleanTargets = true + } else if (arg === '--check') { + options.check = true + } else if (arg === '--write') { + options.check = false } else if (arg === '--group') { const value = args[++i] if (!value || value.startsWith('--')) { @@ -407,25 +585,41 @@ function showHelp() { console.log(` Usage: generate-verifiers [options] -Generates Solidity verifier contracts from compiled Noir circuits -and places them in packages/enclave-contracts/contracts/verifiers/bfv/honk/. +Generates (or verifies) Solidity Honk verifier contracts from compiled Noir +circuits and places them in packages/enclave-contracts/contracts/verifiers/bfv/honk/. + +The Solidity verifiers are committed to git. Test and benchmark flows run +this script with --check so accidental drift between committed verifiers and +current circuit VKs is surfaced as a failure rather than a silent rewrite. Options: + --check Verify committed verifiers match current VKs (no writes). + Exits non-zero on drift. Used by test/benchmark/CI flows. + --write Write/overwrite committed verifiers (this is the default + when neither --check nor --write is passed). --circuits Circuit names (comma-separated). When omitted, generates all circuits. --group Circuit groups (comma-separated: dkg,threshold,recursive_aggregation) - --clean Remove existing verifier directory before generating + --clean Remove existing verifier directory before generating (write mode only). --no-compile Don't compile circuits automatically (fail if not already compiled); - also skips cleaning nargo target dirs (use after build:circuits) - --no-clean-targets Don't delete nargo target dirs before generating verifiers - --dry-run Show what would be generated without doing anything - -h, --help Show this help message + also skips cleaning nargo target dirs (use after build:circuits). + --no-clean-targets Don't delete nargo target dirs before generating verifiers. + --dry-run Show what would be generated without doing anything. + -h, --help Show this help message. Examples: - pnpm generate:verifiers --circuits dkg_aggregator,decryption_aggregator - pnpm generate:verifiers --circuits dkg_aggregator --clean + pnpm generate:verifiers # Rewrite committed verifiers (manual) + pnpm generate:verifiers --check # Verify committed verifiers (CI/tests) + pnpm generate:verifiers --circuits dkg_aggregator # Single circuit + pnpm generate:verifiers --check --no-compile # Verify against existing artifacts only `) } -if (require.main === module) main() +if (require.main === module) { + main().catch((err: unknown) => { + const msg = err instanceof Error ? err.message : String(err) + console.error(`\n❌ ${msg}\n`) + process.exit(1) + }) +} export { VerifierGenerator, GenerateOptions, CircuitGroup, CIRCUIT_GROUPS } diff --git a/tests/integration/lib/prebuild.sh b/tests/integration/lib/prebuild.sh index 8a99d21ed..9571934e7 100755 --- a/tests/integration/lib/prebuild.sh +++ b/tests/integration/lib/prebuild.sh @@ -27,7 +27,12 @@ if [[ "${PROOF_AGGREGATION_ENABLED:-false}" == "true" ]]; then mkdir -p "${INTEGRATION_NOIR}/circuits" (cd "$ROOT_DIR" && pnpm build:circuits --preset insecure-512 -o "${INTEGRATION_NOIR}/circuits") - (cd "$ROOT_DIR" && pnpm generate:verifiers --no-compile --no-clean-targets) + # `--check`: verify the committed Honk Solidity verifiers in + # packages/enclave-contracts/contracts/verifiers/bfv/honk/ match the + # freshly-built circuits' recursive VKs. Fails loudly on drift instead of + # silently rewriting committed contracts mid-test. If this errors, run + # `pnpm generate:verifiers --write` and commit the diff. + (cd "$ROOT_DIR" && pnpm generate:verifiers --check --no-compile --no-clean-targets) if ! command -v jq >/dev/null 2>&1; then echo "jq is required to pin noir/version.json for integration ZK fixtures" >&2 From 92de921ade451917fc83668925e9cd72129e3441 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 22 May 2026 23:53:17 +0200 Subject: [PATCH 37/54] last nits from coderabbit --- .../scripts/extract_crisp_verify_gas.sh | 4 +-- .../benchmarks/scripts/generate_report.sh | 4 +-- circuits/benchmarks/scripts/run_benchmarks.sh | 4 +++ crates/keyshare/src/threshold_keyshare.rs | 36 +++++++++++++++---- .../scripts/benchmarkGasFromRaw.ts | 27 ++++++-------- scripts/generate-verifiers.ts | 8 ++--- 6 files changed, 52 insertions(+), 31 deletions(-) diff --git a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh index 9b1291870..309e79218 100755 --- a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh +++ b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh @@ -165,7 +165,7 @@ ENCLAVE_TEST_EXIT_CODE=0 if [ "$FOLDED_TEST_EXIT_CODE" -ne 0 ]; then echo " [gas] Skipping EVM replay: test_trbfv_actor failed (exit=${FOLDED_TEST_EXIT_CODE})." echo '{}' >"$TMP_JSON_ENCLAVE" -elif [ ! -s "$TMP_JSON_FOLDED" ] || ! jq -e '.dkg_aggregator.proof_hex and .decryption_aggregator.proof_hex' "$TMP_JSON_FOLDED" >/dev/null 2>&1; then +elif [ ! -s "$TMP_JSON_FOLDED" ] || ! jq -e '(.dkg_aggregator.proof_hex != "") and (.decryption_aggregator.proof_hex != "")' "$TMP_JSON_FOLDED" >/dev/null 2>&1; then echo " [gas] Skipping EVM replay: folded proof export missing or empty." echo '{}' >"$TMP_JSON_ENCLAVE" else @@ -320,6 +320,6 @@ if [ "$FOLDED_TEST_EXIT_CODE" -ne 0 ]; then echo " [gas] ERROR: test_trbfv_actor failed — Pi_DKG/Pi_dec verify gas and integration timings will be incomplete." echo " [gas] Re-run after a successful integration export (no Anvil required)." fi -if [ "$FOLDED_TEST_EXIT_CODE" -ne 0 ] || [ "$ENCLAVE_TEST_EXIT_CODE" -ne 0 ]; then +if [ "$CRISP_TEST_EXIT_CODE" -ne 0 ] || [ "$FOLDED_TEST_EXIT_CODE" -ne 0 ] || [ "$ENCLAVE_TEST_EXIT_CODE" -ne 0 ]; then exit 1 fi diff --git a/circuits/benchmarks/scripts/generate_report.sh b/circuits/benchmarks/scripts/generate_report.sh index c302f090c..e11948dac 100755 --- a/circuits/benchmarks/scripts/generate_report.sh +++ b/circuits/benchmarks/scripts/generate_report.sh @@ -277,8 +277,8 @@ integration_timing_seconds() { local label="$1" local val="" local f blob - if [ -n "${3:-}" ]; then - val=$(integration_phase_seconds "$label" "$3") + if [ -n "${2:-}" ]; then + val=$(integration_phase_seconds "$label" "$2") [ -n "$val" ] && [ "$val" != "null" ] && echo "$val" && return fi for f in "$INTEGRATION_SUMMARY_FILE" "$GAS_JSON"; do diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 825439a80..8e57aee8f 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -86,6 +86,10 @@ case "$(echo "$PROOF_AGGREGATION" | tr '[:upper:]' '[:lower:]')" in ;; esac if [ -n "$MULTITHREAD_JOBS" ]; then + if ! [[ "$MULTITHREAD_JOBS" =~ ^[1-9][0-9]*$ ]]; then + echo "Error: --multithread-jobs must be a positive integer (got: $MULTITHREAD_JOBS)" + exit 1 + fi export BENCHMARK_MULTITHREAD_JOBS="$MULTITHREAD_JOBS" fi diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index dee0f6630..de9057edb 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -485,13 +485,26 @@ impl ThresholdKeyshare { }) } + fn keyshare_created_fields( + state: &KeyshareState, + ) -> Option<(&ArcBytes, &Option)> { + use KeyshareState as K; + match state { + K::ReadyForDecryption(s) => Some((&s.pk_share, &s.signed_pk_generation_proof)), + K::Decrypting(s) => Some((&s.pk_share, &s.signed_pk_generation_proof)), + _ => None, + } + } + fn try_finish_deferred_keyshare_publish(&mut self, ec: EventContext) -> Result<()> { if !self.pending_keyshare_publish { return Ok(()); } let state = self.state.try_get()?; - let current: ReadyForDecryption = state.clone().try_into()?; - if current.signed_pk_generation_proof.is_none() { + let Some((_, signed)) = Self::keyshare_created_fields(&state.state) else { + return Ok(()); + }; + if signed.is_none() { return Ok(()); } self.pending_keyshare_publish = false; @@ -2237,9 +2250,20 @@ impl ThresholdKeyshare { let e3_id = state.get_e3_id(); let address = state.get_address().to_owned(); let party_id = state.get_party_id(); - let current: ReadyForDecryption = state.clone().try_into()?; + let Some((pk_share, signed_pk_generation_proof)) = + Self::keyshare_created_fields(&state.state) + else { + warn!( + "Deferring KeyshareCreated for party {} E3 {} — not in ReadyForDecryption/Decrypting ({})", + party_id, + e3_id, + state.state.variant_name() + ); + self.pending_keyshare_publish = true; + return Ok(()); + }; - if current.signed_pk_generation_proof.is_none() { + if signed_pk_generation_proof.is_none() { warn!( "Deferring KeyshareCreated for party {} E3 {} — C1 proof not stored yet (PkGenerationProofSigned race)", party_id, e3_id @@ -2252,11 +2276,11 @@ impl ThresholdKeyshare { self.bus.publish( KeyshareCreated { - pubkey: current.pk_share, + pubkey: pk_share.clone(), e3_id: e3_id.clone(), node: address, party_id, - signed_pk_generation_proof: current.signed_pk_generation_proof, + signed_pk_generation_proof: signed_pk_generation_proof.clone(), }, ec, )?; diff --git a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts index a8bbaa6b1..31608822c 100644 --- a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts @@ -119,24 +119,17 @@ async function main() { } if (!dkgProofHex || !dkgPublicHex || !decProofHex || !decPublicHex) { - const out = { - verify_gas: { dkg: null, user: null, dec: null }, - source: "folded_proof_export_plus_crisp_verify_test", - note: "Missing folded or raw benchmark proofs — run test_trbfv_actor successfully first", - artifact_sizes_bytes: { - dkg: { proof: 0, public_inputs: 0 }, - dec: { proof: 0, public_inputs: 0 }, - }, - calldata_gas: { - dkg: { proof: 0, public_inputs: 0, total: 0 }, - dec: { proof: 0, public_inputs: 0, total: 0 }, - }, - }; - fs.writeFileSync(outputPath, JSON.stringify(out, null, 2) + "\n"); - console.warn( - "[benchmarkGasFromRaw] Wrote placeholder gas JSON (no proofs to replay)", + const missing = [ + !dkgProofHex && "dkg proof", + !dkgPublicHex && "dkg public inputs", + !decProofHex && "decryption proof", + !decPublicHex && "decryption public inputs", + ] + .filter(Boolean) + .join(", "); + throw new Error( + `[benchmarkGasFromRaw] Missing benchmark proofs (${missing}); run test_trbfv_actor successfully first. outputPath=${outputPath}`, ); - return; } const dkgPublicInputs = hexToBytes32Array(dkgPublicHex); diff --git a/scripts/generate-verifiers.ts b/scripts/generate-verifiers.ts index 937fe4483..3a8f93de9 100644 --- a/scripts/generate-verifiers.ts +++ b/scripts/generate-verifiers.ts @@ -34,7 +34,7 @@ * pnpm generate:verifiers --no-compile # Use artifacts from build:circuits (skips target cleanup) */ -import { execSync } from 'child_process' +import { execFileSync, execSync } from 'child_process' import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'fs' import { basename, join, resolve } from 'path' import { ALL_GROUPS, CIRCUIT_GROUPS, type CircuitGroup } from './circuit-constants' @@ -303,7 +303,7 @@ class VerifierGenerator { // 3. Generate Solidity verifier const rawSolPath = join(targetDir, `${packageName}_verifier.sol`) - execSync(`bb write_solidity_verifier -k "${vkPath}" -o "${rawSolPath}"`, { stdio: 'pipe' }) + execFileSync('bb', ['write_solidity_verifier', '-k', vkPath, '-o', rawSolPath], { stdio: 'pipe' }) if (!existsSync(rawSolPath)) { throw new Error('bb write_solidity_verifier did not produce output') @@ -350,7 +350,7 @@ class VerifierGenerator { private formatSolidity(content: string, outputPath: string): string { const contractsDir = join(this.rootDir, 'packages', 'enclave-contracts') // Use prettier --stdin-filepath so plugin selection is by extension. - const result = execSync(`pnpm exec prettier --stdin-filepath "${outputPath}"`, { + const result = execFileSync('pnpm', ['exec', 'prettier', '--stdin-filepath', outputPath], { cwd: contractsDir, input: content, stdio: ['pipe', 'pipe', 'pipe'], @@ -404,7 +404,7 @@ class VerifierGenerator { } // Generate VK (EVM target for Solidity verifiers) - execSync(`bb write_vk -b "${jsonFile}" -o "${targetDir}" -t evm`, { stdio: 'pipe' }) + execFileSync('bb', ['write_vk', '-b', jsonFile, '-o', targetDir, '-t', 'evm'], { stdio: 'pipe' }) // bb writes to 'vk' by default, rename to .vk if (existsSync(defaultVk) && !existsSync(vkFile)) { From ea25dd482380342a1dd6d75a2877827d5885a5db Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Sat, 23 May 2026 10:56:08 +0500 Subject: [PATCH 38/54] fix: resolve merge conflicts --- .../contracts/Enclave.sol/Enclave.json | 6 +- .../IBondingRegistry.json | 6 +- .../ICiphernodeRegistry.json | 15 +- .../interfaces/IEnclave.sol/IEnclave.json | 6 +- .../ISlashingManager.json | 6 +- .../CiphernodeRegistryOwnable.json | 139 ++++++------------ .../EnclaveTicketToken.json | 54 ++----- .../registry/CiphernodeRegistryOwnable.sol | 7 +- .../enclave-contracts/test/Enclave.spec.ts | 1 - .../test/Pricing/DustRotation.spec.ts | 5 +- .../Pricing/PullPaymentsAndAllowlist.spec.ts | 2 +- .../CiphernodeRegistryOwnable.spec.ts | 12 +- .../test/Slashing/CommitteeExpulsion.spec.ts | 14 -- .../test/fixtures/helpers.ts | 2 + 14 files changed, 81 insertions(+), 194 deletions(-) diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 3cbdcf9e4..760d163ff 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -3159,9 +3159,5 @@ }, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", -<<<<<<< HEAD - "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" -======= - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" ->>>>>>> main + "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 85311ebbf..7b1f01d6c 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -1062,9 +1062,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", -<<<<<<< HEAD - "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" -======= - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" ->>>>>>> main + "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 527e07fde..a10a49010 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -70,7 +70,6 @@ "type": "error" }, { -<<<<<<< HEAD "inputs": [], "name": "FoldAttestationVerifierAlreadySet", "type": "error" @@ -86,8 +85,6 @@ "type": "error" }, { -======= ->>>>>>> main "inputs": [ { "internalType": "uint256", @@ -110,14 +107,11 @@ }, { "inputs": [], -<<<<<<< HEAD "name": "InvalidFoldAttestation", "type": "error" }, { "inputs": [], -======= ->>>>>>> main "name": "InvalidTicketNumber", "type": "error" }, @@ -174,7 +168,6 @@ }, { "inputs": [], -<<<<<<< HEAD "name": "PartyIdNotInProof", "type": "error" }, @@ -196,8 +189,6 @@ }, { "inputs": [], -======= ->>>>>>> main "name": "PkCommitmentRequired", "type": "error" }, @@ -1195,9 +1186,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", -<<<<<<< HEAD - "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" -======= - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" ->>>>>>> main + "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index 26d53c9b8..e7d6b70c7 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2427,9 +2427,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", -<<<<<<< HEAD - "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" -======= - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" ->>>>>>> main + "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index 0ee251129..f61c834dd 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -1186,9 +1186,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", -<<<<<<< HEAD - "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" -======= - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" ->>>>>>> main + "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index 01ee7d71a..d3c0362c4 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -80,7 +80,6 @@ "type": "error" }, { -<<<<<<< HEAD "inputs": [], "name": "FoldAttestationVerifierAlreadySet", "type": "error" @@ -96,8 +95,6 @@ "type": "error" }, { -======= ->>>>>>> main "inputs": [ { "internalType": "uint256", @@ -120,14 +117,11 @@ }, { "inputs": [], -<<<<<<< HEAD "name": "InvalidFoldAttestation", "type": "error" }, { "inputs": [], -======= ->>>>>>> main "name": "InvalidInitialization", "type": "error" }, @@ -216,23 +210,13 @@ }, { "inputs": [], -<<<<<<< HEAD "name": "PartyIdNotInProof", -======= - "name": "PkCommitmentRequired", - "type": "error" - }, - { - "inputs": [], - "name": "RenounceOwnershipDisabled", ->>>>>>> main "type": "error" }, { "inputs": [ { "internalType": "uint256", -<<<<<<< HEAD "name": "partyId", "type": "uint256" }, @@ -248,13 +232,22 @@ { "inputs": [], "name": "PkCommitmentRequired", -======= + "type": "error" + }, + { + "inputs": [], + "name": "RenounceOwnershipDisabled", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", "name": "window", "type": "uint256" } ], "name": "SortitionSubmissionWindowOutOfBounds", ->>>>>>> main "type": "error" }, { @@ -775,9 +768,19 @@ }, { "inputs": [], -<<<<<<< HEAD "name": "DKG_FOLD_VERIFIER_TIMELOCK", -======= + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], "name": "MAX_CIPHERNODE_LEAVES", "outputs": [ { @@ -805,7 +808,6 @@ { "inputs": [], "name": "MIN_SORTITION_SUBMISSION_WINDOW", ->>>>>>> main "outputs": [ { "internalType": "uint256", @@ -1368,11 +1370,7 @@ }, { "inputs": [], -<<<<<<< HEAD "name": "pendingDkgFoldAttestationVerifier", -======= - "name": "pendingOwner", ->>>>>>> main "outputs": [ { "internalType": "address", @@ -1384,7 +1382,6 @@ "type": "function" }, { -<<<<<<< HEAD "inputs": [], "name": "pendingDkgFoldAttestationVerifierAt", "outputs": [ @@ -1397,6 +1394,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1411,8 +1421,6 @@ "type": "function" }, { -======= ->>>>>>> main "inputs": [ { "internalType": "uint256", @@ -1719,55 +1727,30 @@ "type": "function" } ], -<<<<<<< HEAD - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b61497a806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102b8575f3560e01c80639a7a2ffc11610177578063da881e5a116100d5578063e82f3b701161008f578063e82f3b7014610694578063ebf0c717146106a7578063f1650536146106af578063f2fde38b146106c9578063f379b0df146106dc578063f52fd80314610716578063f6fc05d514610786575f5ffd5b8063da881e5a14610623578063dbb06c9314610636578063e4be6e3d14610648578063e4d185db1461065b578063e59e46951461066e578063e6745e1314610681575f5ffd5b8063b8ab470411610131578063b8ab470414610584578063bff232c1146105a6578063c2b40ae4146105b9578063c3a0ec30146105d8578063c8fe182d146105e9578063ca2869a0146105f1578063cd6dc68714610610575f5ffd5b80639a7a2ffc146104f05780639f0f874a1461052c578063a016493014610535578063a8a4d69b14610555578063acc5249414610568578063b2d5d1ac1461057b575f5ffd5b806350e6fad31161022457806385814243116101de57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ac5780638da5cb5b146104c25780638e5ce3ad146104ca5780639015d371146104dd575f5ffd5b806350e6fad3146103e25780635d504776146103ec57806362cc89a8146103ff57806370e36bbe1461041f578063715018a6146104325780637c92f5241461043a575f5ffd5b80632800d829116102755780632800d82914610351578063291a691b146103645780632e7b716d14610387578063323beaa51461039a5780634d6861a6146103ad57806350e6d94c146103c0575f5ffd5b8063096b810a146102bc578063099a161a146102d15780630b45f8e9146102f75780630bbfade71461030a5780630f3e34121461031d57806317d6112014610330575b5f5ffd5b6102cf6102ca366004613d12565b61078f565b005b6102e46102df366004613d2d565b6108db565b6040519081526020015b60405180910390f35b6102cf610305366004613d12565b610914565b6102cf610318366004613d88565b610a19565b6102cf61032b366004613d2d565b610c5d565b61034361033e366004613d2d565b610ca0565b6040516102ee929190613ea9565b6102e461035f366004613d2d565b610e49565b610377610372366004613ed6565b610e95565b60405190151581526020016102ee565b610377610395366004613d12565b61106f565b6102cf6103a8366004613d12565b611122565b6103776103bb366004613d2d565b611233565b6103776103ce366004613d12565b60066020525f908152604090205460ff1681565b6102e46202a30081565b6103776103fa366004613f0f565b611272565b600d54610412906001600160a01b031681565b6040516102ee9190613f3d565b6102cf61042d366004613d12565b6112b5565b6102cf61132b565b61044d610448366004613f51565b61133e565b6040805192835263ffffffff9091166020830152016102ee565b600154610412906001600160a01b031681565b6102cf610488366004613d12565b6114e5565b6102e461049b366004613d2d565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102e4565b610412611623565b600b54610412906001600160a01b031681565b6103776104eb366004613d12565b611651565b6105166104fe366004613d12565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ee565b6102e460035481565b610548610543366004613d2d565b61166e565b6040516102ee9190613f86565b610377610563366004613f0f565b611704565b6102cf610576366004613d12565b611747565b6102e4600e5481565b610597610592366004613d2d565b6117de565b6040516102ee93929190613f98565b6102cf6105b4366004613d12565b611929565b6102e46105c7366004613d2d565b60086020525f908152604090205481565b6001546001600160a01b0316610412565b6102cf6119a1565b6102e46105ff366004613d2d565b5f9081526008602052604090205490565b6102cf61061e366004613fda565b611a1d565b610377610631366004613d2d565b611b7a565b5f54610412906001600160a01b031681565b600c54610412906001600160a01b031681565b610412610669366004614004565b611e5d565b6102cf61067c366004613d12565b611f03565b6102cf61068f366004614004565b611f7b565b6102e46106a2366004613d2d565b61213e565b6102e461216f565b6106b7601481565b60405160ff90911681526020016102ee565b6102cf6106d7366004613d12565b612181565b6004546106f89064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ee565b610757610724366004613d2d565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ee949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e460025481565b610797611623565b6001600160a01b0316336001600160a01b031614806107c057506001546001600160a01b031633145b6107dd57604051632864c4e160e01b815260040160405180910390fd5b6107e681611651565b819061080f576040516381e5828960e01b81526004016108069190613f3d565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff169061083d90600490836121bb565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161086a83614038565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a60205260408120600481015461090a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61091c61245d565b600c546001600160a01b0316156109455760405162035f5560e61b815260040160405180910390fd5b6001600160a01b03811661096c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d5416156109e357600d80546001600160a01b031981169091555f600e8190556040516001600160a01b039092169182917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a2505b6040516001600160a01b038216907f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b905f90a250565b5f888152600a602052604090206002815460ff166003811115610a3e57610a3e61404d565b14610a5c57604051634f4b461f60e11b815260040160405180910390fd5b600481015415610a7f5760405163632a22bb60e01b815260040160405180910390fd5b85610a9d57604051636caad1ed60e11b815260040160405180910390fd5b5f610b0182600601805480602002602001604051908101604052809291908181526020018280548015610af757602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610ad9575b505050505061248f565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610b53573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b7a91908101906141dd565b9050806101c0015115610b9757610b978b828a858b8b8b8b612595565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610bf5575f5ffd5b505af1158015610c07573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610c489695949392919061439f565b60405180910390a25050505050505050505050565b610c6561245d565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610cd657610cd6614061565b604051908082528060200260200182016040528015610cff578160200160208202803683370190505b509450806001600160401b03811115610d1a57610d1a614061565b604051908082528060200260200182016040528015610d43578160200160208202803683370190505b5093505f805b83811015610e3f575f856006018281548110610d6757610d676143ec565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610dad57610dad61404d565b03610e365780888481518110610dc557610dc56143ec565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610e1d57610e1d6143ec565b602090810291909101015282610e3281614400565b9350505b50600101610d49565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610e6d57610e6d61404d565b03610e8b57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610ec05760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610ee457610ee461404d565b14610f02576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610f49573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f6d9190614418565b905080610f806040860160208701614442565b63ffffffff161115610f986040860160208701614442565b829091610fc6576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610806565b5050815460ff1916600190811783558201859055436002830155600354610fed904261445b565b600383015561100160058301856002613c10565b5061100a61216f565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a1709261105b928a928a929161446e565b60405180910390a250600195945050505050565b5f61107982611651565b61108457505f919050565b6001546001600160a01b03166110ad576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906110dd908590600401613f3d565b602060405180830381865afa1580156110f8573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061111c91906144be565b92915050565b61112a61245d565b600d546001600160a01b0316806111545760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461119557604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610806565b50505f6202a300600e546111a9919061445b565b90508042818110156111d7576040516337c8270b60e01b815260048101929092526024820152604401610806565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690555f600e8190556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b9190a2505050565b5f818152600a602052604081206001815460ff1660038111156112585761125861404d565b1461126557505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156112ad576112ad61404d565b149392505050565b6112bd61245d565b6001600160a01b0381166112e45760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61133361245d565b61133c5f612791565b565b600b545f9081906001600160a01b0316331461136d5760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff1660038111156113925761139261404d565b146113b057604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff1660028111156113f0576113f061404d565b1461140057600b015491506114dd565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b820180549161143483614038565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611485929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6114ed611623565b6001600160a01b0316336001600160a01b0316148061151657506001546001600160a01b031633145b61153357604051632864c4e160e01b815260040160405180910390fd5b61153c81611651565b6116205760048054600160281b900464ffffffffff1690611566906001600160a01b038416612801565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916115b783614400565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016108cf565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906116a1576040516322e679e360e11b815260040160405180910390fd5b806006018054806020026020016040519081016040528092919081815260200182805480156116f757602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116116d9575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561173e5761173e61404d565b14159392505050565b61174f61245d565b6001600160a01b0381166117765760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f25906117ca906202a3009061445b565b60405190815260200160405180910390a250565b5f8181526009602052604090205460609081908190611810576040516322e679e360e11b815260040160405180910390fd5b5f848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561187557602002820191905f5260205f20905b815481526020019060010190808311611861575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156118c557602002820191905f5260205f20905b8154815260200190600101908083116118b1575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561191557602002820191905f5260205f20905b815481526020019060010190808311611901575b505050505090509250925092509193909250565b61193161245d565b6001600160a01b0381166119585760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6119a961245d565b600d546001600160a01b0316806119d35760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690555f600e8190556040516001600160a01b038316917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a250565b5f611a266129d7565b805490915060ff600160401b82041615906001600160401b03165f81158015611a4c5750825b90505f826001600160401b03166001148015611a675750303b155b905081158015611a75575080155b15611a935760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611abd57845460ff60401b1916600160401b1785555b6001600160a01b038716611ae45760405163d92e233d60e01b815260040160405180910390fd5b611aed336129ff565b611af960046014612a10565b611b0286610c5d565b611b0a611623565b6001600160a01b0316876001600160a01b031614611b2b57611b2b87612181565b8315611b7157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff166003811115611b9e57611b9e61404d565b03611bbc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611bd457611bd461404d565b14611bf257604051631860f69960e31b815260040160405180910390fd5b80600301544211611c1657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611cfb578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611cdc575f5ffd5b505af1158015611cee573d5f5f3e3d5ffd5b505f979650505050505050565b611d0482612a8f565b815460ff191660021782556006820154600b83018190555f816001600160401b03811115611d3457611d34614061565b604051908082528060200260200182016040528015611d5d578160200160208202803683370190505b5090505f5b82811015611dcf57846009015f866006018381548110611d8457611d846143ec565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611dbc57611dbc6143ec565b6020908102919091010152600101611d62565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611e12575f5ffd5b505af1158015611e24573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d7856006018360405161105b9291906144d7565b5f828152600a602052604081206002815460ff166003811115611e8257611e8261404d565b14611ea057604051634f4b461f60e11b815260040160405180910390fd5b60068101548390808210611ed0576040516326c5c55b60e11b815260048101929092526024820152604401610806565b5050806006018381548110611ee757611ee76143ec565b5f918252602090912001546001600160a01b0316949350505050565b611f0b61245d565b6001600160a01b038116611f325760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611f9f57611f9f61404d565b03611fbd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611fd557611fd561404d565b14611ff357604051631860f69960e31b815260040160405180910390fd5b806003015442111561201857604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff161561204a5760405163257309f160e11b815260040160405180910390fd5b6120533361106f565b6120705760405163149fbcfd60e11b815260040160405180910390fd5b61207b338385612bb5565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff191660011790559091506120fa90839083612d86565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f818152600960205260409020548061216a576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61217c60046014612f87565b905090565b61218961245d565b6001600160a01b0381166121b2575f604051631e4fbdf760e01b81526004016108069190613f3d565b61162081612791565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106121fa5760405162461bcd60e51b8152600401610806906144e9565b825464ffffffffff600160281b909104811690821681116122585760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610806565b825f5b81866001015f61226b8488613080565b64ffffffffff1681526020019081526020015f20819055505f8160016122919190614533565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116122c65750612455565b600185165f0361238d575f6122e5836122e088600161454c565b613080565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161234691600401614569565b602060405180830381865af4158015612361573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123859190614418565b935050612441565b5f61239d836122e0600189614599565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916123fe91600401614569565b602060405180830381865af4158015612419573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061243d9190614418565b9350505b50647fffffffff600194851c16930161225b565b505050505050565b33612466611623565b6001600160a01b03161461133c573360405163118cdaa760e01b81526004016108069190613f3d565b80515f908161249f8260146145b6565b6001600160401b038111156124b6576124b6614061565b6040519080825280601f01601f1916602001820160405280156124e0576020820181803683370190505b5090505f5b82811015612585575f858281518110612500576125006143ec565b602002602001015160601b90505f82601461251b91906145b6565b90505f5b60148110156125775782816014811061253a5761253a6143ec565b1a60f81b85612549838561445b565b81518110612559576125596143ec565b60200101906001600160f81b03191690815f1a90535060010161251f565b5050508060010190506124e5565b5080516020909101209392505050565b826125b357604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b81526004016125ea94939291906145cd565b602060405180830381865afa158015612605573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061262991906144be565b6126465760405163051d8aa760e51b815260040160405180910390fd5b8061266457604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661268d57604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b81526004016126dc97969594939291906145ec565b5f60405180830381865afa1580156126f6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261271d91908101906146c6565b5f8e8152600f6020908152604090912084519497509295509093506127459290860190613cb1565b505f8b8152601060209081526040909120835161276492850190613cb1565b505f8b8152601160209081526040909120825161278392840190613cb1565b505050505050505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106128505760405162461bcd60e51b8152600401610806906144e9565b825464ffffffffff908116908216106128a35760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610806565b6128ae81600161454c565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6128e58487613080565b64ffffffffff16815260208101919091526040015f205560018316156129d0575f612915826122e0600187614599565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161297691600401614569565b602060405180830381865af4158015612991573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129b59190614418565b647fffffffff600195861c16949093509190910190506128d5565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061111c565b612a0761309d565b611620816130c2565b602060ff82161115612a5e5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610806565b612a6f600160ff831681901b6147aa565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612bb0575f612aaa82600161445b565b90505b82811015612ba7575f846006018381548110612acb57612acb6143ec565b5f9182526020822001546006870180546001600160a01b0390921693509084908110612af957612af96143ec565b5f918252602090912001546001600160a01b0390811691508216811015612b9d5780866006018581548110612b3057612b306143ec565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612b7157612b716143ec565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612aad565b50600101612a96565b505050565b5f8211612bd55760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612bfe576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612c34916147aa565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612c7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c9f9190614418565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cf2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d169190614418565b90505f8111612d385760405163aeaddff160e01b815260040160405180910390fd5b5f612d4382846147bd565b90505f8111612d655760405163149fbcfd60e11b815260040160405180910390fd5b80861115611b715760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612e0457508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612f80565b5f5f90505f876009015f855f81548110612e2057612e206143ec565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612ea8575f896009015f878481548110612e6a57612e6a6143ec565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e9f578092508193505b50600101612e49565b50808610612ebc575f945050505050612f80565b5f88600a015f868581548110612ed457612ed46143ec565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612f1157612f1161404d565b021790555086848381548110612f2957612f296143ec565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612fda5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610806565b602060ff83161115612ffe5760405162461bcd60e51b8152600401610806906147dc565b8254600160281b900464ffffffffff168061301d60ff8516600261492d565b64ffffffffff16101561306d5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610806565b6130788482856130ca565b949350505050565b5f8161309360ff851663ffffffff614946565b612f80919061454c565b6130a5613192565b61133c57604051631afcd79f60e31b815260040160405180910390fd5b61218961309d565b5f602060ff831611156130ef5760405162461bcd60e51b8152600401610806906147dc565b8264ffffffffff165f0361310d57613106826131ab565b9050612f80565b5f613119836001614533565b60ff166001600160401b0381111561313357613133614061565b60405190808252806020026020018201604052801561315c578160200160208202803683370190505b50905061316b85858584613845565b808360ff1681518110613180576131806143ec565b60200260200101519150509392505050565b5f61319b6129d7565b54600160401b900460ff16919050565b5f8160ff165f036131bd57505f919050565b8160ff166001036131ef57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361322157507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361325357507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361328557507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036132b757507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036132e957507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361331b57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361334d57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361337f57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036133b157507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036133e357507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361341557507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361344757507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361347957507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036134ab57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036134dd57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361350f57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361354157507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361357357507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036135a557507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036135d757507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361360957507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361363b57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361366d57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361369f57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036136d157507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361370357507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361373557507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361376757507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361379957507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f036137cb57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036137fd57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610806565b602060ff831611156138695760405162461bcd60e51b8152600401610806906147dc565b5f8364ffffffffff16116138cd5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610806565b5f6138d9600185614599565b9050600181165f0361392c57846001015f6138f45f84613080565b64ffffffffff1681526020019081526020015f2054825f8151811061391b5761391b6143ec565b602002602001018181525050613954565b6139355f6131ab565b825f81518110613947576139476143ec565b6020026020010181815250505b5f5b8360ff168160ff16101561245557600182165f03613a4c5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff16815181106139a8576139a86143ec565b602002602001015181526020016139be856131ab565b8152506040518263ffffffff1660e01b81526004016139dd9190614569565b602060405180830381865af41580156139f8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613a1c9190614418565b83613a28836001614533565b60ff1681518110613a3b57613a3b6143ec565b602002602001018181525050613bfd565b5f613a58826001614533565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613afa575f876001015f613aaf856001613a9e9190614533565b60018864ffffffffff16901c613080565b64ffffffffff1681526020019081526020015f205490508085846001613ad59190614533565b60ff1681518110613ae857613ae86143ec565b60200260200101818152505050613bfb565b5f876001015f613b11856001886122e09190614599565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613b6857613b686143ec565b60200260200101518152506040518263ffffffff1660e01b8152600401613b8f9190614569565b602060405180830381865af4158015613baa573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bce9190614418565b85613bda856001614533565b60ff1681518110613bed57613bed6143ec565b602002602001018181525050505b505b647fffffffff600192831c169101613956565b600183019183908215613ca1579160200282015f5b83821115613c6f57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613c25565b8015613c9f5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613c6f565b505b50613cad929150613cea565b5090565b828054828255905f5260205f20908101928215613ca1579160200282015b82811115613ca1578251825591602001919060010190613ccf565b5b80821115613cad575f8155600101613ceb565b6001600160a01b0381168114611620575f5ffd5b5f60208284031215613d22575f5ffd5b8135612f8081613cfe565b5f60208284031215613d3d575f5ffd5b5035919050565b5f5f83601f840112613d54575f5ffd5b5081356001600160401b03811115613d6a575f5ffd5b602083019150836020828501011115613d81575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613d9f575f5ffd5b8835975060208901356001600160401b03811115613dbb575f5ffd5b613dc78b828c01613d44565b9098509650506040890135945060608901356001600160401b03811115613dec575f5ffd5b613df88b828c01613d44565b90955093505060808901356001600160401b03811115613e16575f5ffd5b613e228b828c01613d44565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613e6f5781516001600160a01b0316865260209586019590910190600101613e48565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613e6f578151865260209586019590910190600101613e8b565b604081525f613ebb6040830185613e36565b8281036020840152613ecd8185613e79565b95945050505050565b5f5f5f60808486031215613ee8575f5ffd5b833592506020840135915060808401851015613f02575f5ffd5b6040840190509250925092565b5f5f60408385031215613f20575f5ffd5b823591506020830135613f3281613cfe565b809150509250929050565b6001600160a01b0391909116815260200190565b5f5f5f60608486031215613f63575f5ffd5b833592506020840135613f7581613cfe565b929592945050506040919091013590565b602081525f612f806020830184613e36565b606081525f613faa6060830186613e79565b8281036020840152613fbc8186613e79565b90508281036040840152613fd08185613e79565b9695505050505050565b5f5f60408385031215613feb575f5ffd5b8235613ff681613cfe565b946020939093013593505050565b5f5f60408385031215614015575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161404657614046614024565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b038111828210171561409857614098614061565b60405290565b604051601f8201601f191681016001600160401b03811182821017156140c6576140c6614061565b604052919050565b80516004811061216a575f5ffd5b5f82601f8301126140eb575f5ffd5b604080519081016001600160401b038111828210171561410d5761410d614061565b8060405250806040840185811115614123575f5ffd5b845b8181101561413d578051835260209283019201614125565b509195945050505050565b805161216a81613cfe565b805160ff8116811461216a575f5ffd5b5f82601f830112614172575f5ffd5b81516001600160401b0381111561418b5761418b614061565b61419e601f8201601f191660200161409e565b8181528460208386010111156141b2575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b8051801515811461216a575f5ffd5b5f602082840312156141ed575f5ffd5b81516001600160401b03811115614202575f5ffd5b82016102008185031215614214575f5ffd5b61421c614075565b8151815261422c602083016140ce565b60208201526040828101519082015261424885606084016140dc565b606082015260a0820151608082015261426360c08301614148565b60a082015261427460e08301614153565b60c08201526101008201516001600160401b03811115614292575f5ffd5b61429e86828501614163565b60e0830152506142b16101208301614148565b6101008201526142c46101408301614148565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b038111156142fa575f5ffd5b61430686828501614163565b6101808301525061431a6101c08301614148565b6101a082015261432d6101e083016141ce565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613e6f5781546001600160a01b0316865260209095019460019182019101614350565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f6143b1608083018961433b565b82810360208401526143c481888a614377565b905085604084015282810360608401526143df818587614377565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161441157614411614024565b5060010190565b5f60208284031215614428575f5ffd5b5051919050565b803563ffffffff8116811461216a575f5ffd5b5f60208284031215614452575f5ffd5b612f808261442f565b8082018082111561111c5761111c614024565b84815260a0810160208201855f5b60028110156144a95763ffffffff6144938361442f565b168352602092830192919091019060010161447c565b50505060608201939093526080015292915050565b5f602082840312156144ce575f5ffd5b612f80826141ce565b604081525f613ebb604083018561433b565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff818116838216019081111561111c5761111c614024565b64ffffffffff818116838216019081111561111c5761111c614024565b6040810181835f5b6002811015614590578151835260209283019290910190600101614571565b50505092915050565b64ffffffffff828116828216039081111561111c5761111c614024565b808202811582820484141761111c5761111c614024565b848152836020820152606060408201525f613fd0606083018486614377565b60018060a01b038816815286602082015285604082015260a060608201525f61461960a083018688614377565b828103608084015261462c818587614377565b9a9950505050505050505050565b5f6001600160401b0382111561465257614652614061565b5060051b60200190565b5f82601f83011261466b575f5ffd5b815161467e6146798261463a565b61409e565b8082825260208201915060208360051b86010192508583111561469f575f5ffd5b602085015b838110156146bc5780518352602092830192016146a4565b5095945050505050565b5f5f5f606084860312156146d8575f5ffd5b83516001600160401b038111156146ed575f5ffd5b8401601f810186136146fd575f5ffd5b805161470b6146798261463a565b8082825260208201915060208360051b85010192508883111561472c575f5ffd5b6020840193505b8284101561474e578351825260209384019390910190614733565b8096505050505060208401516001600160401b0381111561476d575f5ffd5b6147798682870161465c565b92505060408401516001600160401b03811115614794575f5ffd5b6147a08682870161465c565b9150509250925092565b8181038181111561111c5761111c614024565b5f826147d757634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156114dd5780850481111561483e5761483e614024565b600184161561484c57908102905b60019390931c928002614823565b5f826148685750600161111c565b8161487457505f61111c565b816001811461488a5760028114614894576148c6565b600191505061111c565b60ff8411156148a5576148a5614024565b6001841b915064ffffffffff8211156148c0576148c0614024565b5061111c565b5060208310610133831016604e8410600b84101617156148fe575081810a64ffffffffff8111156148f9576148f9614024565b61111c565b61490e64ffffffffff848461481f565b8064ffffffffff0482111561492557614925614024565b029392505050565b5f612f8064ffffffffff841664ffffffffff841661485a565b64ffffffffff818116838216029081169081811461496657614966614024565b509291505056fea164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102b8575f3560e01c80639a7a2ffc11610177578063da881e5a116100d5578063e82f3b701161008f578063e82f3b7014610694578063ebf0c717146106a7578063f1650536146106af578063f2fde38b146106c9578063f379b0df146106dc578063f52fd80314610716578063f6fc05d514610786575f5ffd5b8063da881e5a14610623578063dbb06c9314610636578063e4be6e3d14610648578063e4d185db1461065b578063e59e46951461066e578063e6745e1314610681575f5ffd5b8063b8ab470411610131578063b8ab470414610584578063bff232c1146105a6578063c2b40ae4146105b9578063c3a0ec30146105d8578063c8fe182d146105e9578063ca2869a0146105f1578063cd6dc68714610610575f5ffd5b80639a7a2ffc146104f05780639f0f874a1461052c578063a016493014610535578063a8a4d69b14610555578063acc5249414610568578063b2d5d1ac1461057b575f5ffd5b806350e6fad31161022457806385814243116101de57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ac5780638da5cb5b146104c25780638e5ce3ad146104ca5780639015d371146104dd575f5ffd5b806350e6fad3146103e25780635d504776146103ec57806362cc89a8146103ff57806370e36bbe1461041f578063715018a6146104325780637c92f5241461043a575f5ffd5b80632800d829116102755780632800d82914610351578063291a691b146103645780632e7b716d14610387578063323beaa51461039a5780634d6861a6146103ad57806350e6d94c146103c0575f5ffd5b8063096b810a146102bc578063099a161a146102d15780630b45f8e9146102f75780630bbfade71461030a5780630f3e34121461031d57806317d6112014610330575b5f5ffd5b6102cf6102ca366004613d12565b61078f565b005b6102e46102df366004613d2d565b6108db565b6040519081526020015b60405180910390f35b6102cf610305366004613d12565b610914565b6102cf610318366004613d88565b610a19565b6102cf61032b366004613d2d565b610c5d565b61034361033e366004613d2d565b610ca0565b6040516102ee929190613ea9565b6102e461035f366004613d2d565b610e49565b610377610372366004613ed6565b610e95565b60405190151581526020016102ee565b610377610395366004613d12565b61106f565b6102cf6103a8366004613d12565b611122565b6103776103bb366004613d2d565b611233565b6103776103ce366004613d12565b60066020525f908152604090205460ff1681565b6102e46202a30081565b6103776103fa366004613f0f565b611272565b600d54610412906001600160a01b031681565b6040516102ee9190613f3d565b6102cf61042d366004613d12565b6112b5565b6102cf61132b565b61044d610448366004613f51565b61133e565b6040805192835263ffffffff9091166020830152016102ee565b600154610412906001600160a01b031681565b6102cf610488366004613d12565b6114e5565b6102e461049b366004613d2d565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102e4565b610412611623565b600b54610412906001600160a01b031681565b6103776104eb366004613d12565b611651565b6105166104fe366004613d12565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102ee565b6102e460035481565b610548610543366004613d2d565b61166e565b6040516102ee9190613f86565b610377610563366004613f0f565b611704565b6102cf610576366004613d12565b611747565b6102e4600e5481565b610597610592366004613d2d565b6117de565b6040516102ee93929190613f98565b6102cf6105b4366004613d12565b611929565b6102e46105c7366004613d2d565b60086020525f908152604090205481565b6001546001600160a01b0316610412565b6102cf6119a1565b6102e46105ff366004613d2d565b5f9081526008602052604090205490565b6102cf61061e366004613fda565b611a1d565b610377610631366004613d2d565b611b7a565b5f54610412906001600160a01b031681565b600c54610412906001600160a01b031681565b610412610669366004614004565b611e5d565b6102cf61067c366004613d12565b611f03565b6102cf61068f366004614004565b611f7b565b6102e46106a2366004613d2d565b61213e565b6102e461216f565b6106b7601481565b60405160ff90911681526020016102ee565b6102cf6106d7366004613d12565b612181565b6004546106f89064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102ee565b610757610724366004613d2d565b5f908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102ee949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e460025481565b610797611623565b6001600160a01b0316336001600160a01b031614806107c057506001546001600160a01b031633145b6107dd57604051632864c4e160e01b815260040160405180910390fd5b6107e681611651565b819061080f576040516381e5828960e01b81526004016108069190613f3d565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff169061083d90600490836121bb565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161086a83614038565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b5f818152600a60205260408120600481015461090a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61091c61245d565b600c546001600160a01b0316156109455760405162035f5560e61b815260040160405180910390fd5b6001600160a01b03811661096c5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d5416156109e357600d80546001600160a01b031981169091555f600e8190556040516001600160a01b039092169182917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a2505b6040516001600160a01b038216907f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b905f90a250565b5f888152600a602052604090206002815460ff166003811115610a3e57610a3e61404d565b14610a5c57604051634f4b461f60e11b815260040160405180910390fd5b600481015415610a7f5760405163632a22bb60e01b815260040160405180910390fd5b85610a9d57604051636caad1ed60e11b815260040160405180910390fd5b5f610b0182600601805480602002602001604051908101604052809291908181526020018280548015610af757602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311610ad9575b505050505061248f565b600783018190555f805460405163101bb4d760e21b8152600481018e905292935090916001600160a01b039091169063406ed35c906024015f60405180830381865afa158015610b53573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b7a91908101906141dd565b9050806101c0015115610b9757610b978b828a858b8b8b8b612595565b60048381018990555f8c8152600960205260408082208b9055905490516340a3b76160e11b81529182018d9052602482018a90526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015610bf5575f5ffd5b505af1158015610c07573d5f5f3e3d5ffd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610c489695949392919061439f565b60405180910390a25050505050505050505050565b610c6561245d565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610cd657610cd6614061565b604051908082528060200260200182016040528015610cff578160200160208202803683370190505b509450806001600160401b03811115610d1a57610d1a614061565b604051908082528060200260200182016040528015610d43578160200160208202803683370190505b5093505f805b83811015610e3f575f856006018281548110610d6757610d676143ec565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f908152600a8801602052604090205460ff166002811115610dad57610dad61404d565b03610e365780888481518110610dc557610dc56143ec565b60200260200101906001600160a01b031690816001600160a01b031681525050856009015f826001600160a01b03166001600160a01b031681526020019081526020015f2054878481518110610e1d57610e1d6143ec565b602090810291909101015282610e3281614400565b9350505b50600101610d49565b5050505050915091565b5f818152600a6020526040812081815460ff166003811115610e6d57610e6d61404d565b03610e8b57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610ec05760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610ee457610ee461404d565b14610f02576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610f49573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f6d9190614418565b905080610f806040860160208701614442565b63ffffffff161115610f986040860160208701614442565b829091610fc6576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610806565b5050815460ff1916600190811783558201859055436002830155600354610fed904261445b565b600383015561100160058301856002613c10565b5061100a61216f565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a1709261105b928a928a929161446e565b60405180910390a250600195945050505050565b5f61107982611651565b61108457505f919050565b6001546001600160a01b03166110ad576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906110dd908590600401613f3d565b602060405180830381865afa1580156110f8573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061111c91906144be565b92915050565b61112a61245d565b600d546001600160a01b0316806111545760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461119557604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610806565b50505f6202a300600e546111a9919061445b565b90508042818110156111d7576040516337c8270b60e01b815260048101929092526024820152604401610806565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690555f600e8190556040517f69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b9190a2505050565b5f818152600a602052604081206001815460ff1660038111156112585761125861404d565b1461126557505f92915050565b6003015442111592915050565b5f60015f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156112ad576112ad61404d565b149392505050565b6112bd61245d565b6001600160a01b0381166112e45760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61133361245d565b61133c5f612791565b565b600b545f9081906001600160a01b0316331461136d5760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff1660038111156113925761139261404d565b146113b057604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f908152600a8301602052604090205463ffffffff909116925060019060ff1660028111156113f0576113f061404d565b1461140057600b015491506114dd565b6001600160a01b0385165f908152600a820160205260408120805460ff19166002179055600b820180549161143483614038565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611485929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6114ed611623565b6001600160a01b0316336001600160a01b0316148061151657506001546001600160a01b031633145b61153357604051632864c4e160e01b815260040160405180910390fd5b61153c81611651565b6116205760048054600160281b900464ffffffffff1690611566906001600160a01b038416612801565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916115b783614400565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53906060016108cf565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906116a1576040516322e679e360e11b815260040160405180910390fd5b806006018054806020026020016040519081016040528092919081815260200182805480156116f757602002820191905f5260205f20905b81546001600160a01b031681526001909101906020018083116116d9575b5050505050915050919050565b5f805f848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561173e5761173e61404d565b14159392505050565b61174f61245d565b6001600160a01b0381166117765760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f25906117ca906202a3009061445b565b60405190815260200160405180910390a250565b5f8181526009602052604090205460609081908190611810576040516322e679e360e11b815260040160405180910390fd5b5f848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561187557602002820191905f5260205f20905b815481526020019060010190808311611861575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156118c557602002820191905f5260205f20905b8154815260200190600101908083116118b1575b505050505091508080548060200260200160405190810160405280929190818152602001828054801561191557602002820191905f5260205f20905b815481526020019060010190808311611901575b505050505090509250925092509193909250565b61193161245d565b6001600160a01b0381166119585760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6119a961245d565b600d546001600160a01b0316806119d35760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690555f600e8190556040516001600160a01b038316917f70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c91a250565b5f611a266129d7565b805490915060ff600160401b82041615906001600160401b03165f81158015611a4c5750825b90505f826001600160401b03166001148015611a675750303b155b905081158015611a75575080155b15611a935760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611abd57845460ff60401b1916600160401b1785555b6001600160a01b038716611ae45760405163d92e233d60e01b815260040160405180910390fd5b611aed336129ff565b611af960046014612a10565b611b0286610c5d565b611b0a611623565b6001600160a01b0316876001600160a01b031614611b2b57611b2b87612181565b8315611b7157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff166003811115611b9e57611b9e61404d565b03611bbc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611bd457611bd461404d565b14611bf257604051631860f69960e31b815260040160405180910390fd5b80600301544211611c1657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611cfb578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611cdc575f5ffd5b505af1158015611cee573d5f5f3e3d5ffd5b505f979650505050505050565b611d0482612a8f565b815460ff191660021782556006820154600b83018190555f816001600160401b03811115611d3457611d34614061565b604051908082528060200260200182016040528015611d5d578160200160208202803683370190505b5090505f5b82811015611dcf57846009015f866006018381548110611d8457611d846143ec565b5f9182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611dbc57611dbc6143ec565b6020908102919091010152600101611d62565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611e12575f5ffd5b505af1158015611e24573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d7856006018360405161105b9291906144d7565b5f828152600a602052604081206002815460ff166003811115611e8257611e8261404d565b14611ea057604051634f4b461f60e11b815260040160405180910390fd5b60068101548390808210611ed0576040516326c5c55b60e11b815260048101929092526024820152604401610806565b5050806006018381548110611ee757611ee76143ec565b5f918252602090912001546001600160a01b0316949350505050565b611f0b61245d565b6001600160a01b038116611f325760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff166003811115611f9f57611f9f61404d565b03611fbd57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611fd557611fd561404d565b14611ff357604051631860f69960e31b815260040160405180910390fd5b806003015442111561201857604051639a19114d60e01b815260040160405180910390fd5b335f90815260088201602052604090205460ff161561204a5760405163257309f160e11b815260040160405180910390fd5b6120533361106f565b6120705760405163149fbcfd60e11b815260040160405180910390fd5b61207b338385612bb5565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526008850160205260409020805460ff191660011790559091506120fa90839083612d86565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f818152600960205260409020548061216a576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61217c60046014612f87565b905090565b61218961245d565b6001600160a01b0381166121b2575f604051631e4fbdf760e01b81526004016108069190613f3d565b61162081612791565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106121fa5760405162461bcd60e51b8152600401610806906144e9565b825464ffffffffff600160281b909104811690821681116122585760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b6044820152606401610806565b825f5b81866001015f61226b8488613080565b64ffffffffff1681526020019081526020015f20819055505f8160016122919190614533565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116122c65750612455565b600185165f0361238d575f6122e5836122e088600161454c565b613080565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161234691600401614569565b602060405180830381865af4158015612361573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123859190614418565b935050612441565b5f61239d836122e0600189614599565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916123fe91600401614569565b602060405180830381865af4158015612419573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061243d9190614418565b9350505b50647fffffffff600194851c16930161225b565b505050505050565b33612466611623565b6001600160a01b03161461133c573360405163118cdaa760e01b81526004016108069190613f3d565b80515f908161249f8260146145b6565b6001600160401b038111156124b6576124b6614061565b6040519080825280601f01601f1916602001820160405280156124e0576020820181803683370190505b5090505f5b82811015612585575f858281518110612500576125006143ec565b602002602001015160601b90505f82601461251b91906145b6565b90505f5b60148110156125775782816014811061253a5761253a6143ec565b1a60f81b85612549838561445b565b81518110612559576125596143ec565b60200101906001600160f81b03191690815f1a90535060010161251f565b5050508060010190506124e5565b5080516020909101209392505050565b826125b357604051630fb0193f60e41b815260040160405180910390fd5b8661012001516001600160a01b031663de12c640878787876040518563ffffffff1660e01b81526004016125ea94939291906145cd565b602060405180830381865afa158015612605573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061262991906144be565b6126465760405163051d8aa760e51b815260040160405180910390fd5b8061266457604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661268d57604051630d6eeecb60e01b815260040160405180910390fd5b5f5f5f600c5f9054906101000a90046001600160a01b03166001600160a01b031663b476c31e30468e8b8b8b8b6040518863ffffffff1660e01b81526004016126dc97969594939291906145ec565b5f60405180830381865afa1580156126f6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261271d91908101906146c6565b5f8e8152600f6020908152604090912084519497509295509093506127459290860190613cb1565b505f8b8152601060209081526040909120835161276492850190613cb1565b505f8b8152601160209081526040909120825161278392840190613cb1565b505050505050505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182106128505760405162461bcd60e51b8152600401610806906144e9565b825464ffffffffff908116908216106128a35760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b6044820152606401610806565b6128ae81600161454c565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f6128e58487613080565b64ffffffffff16815260208101919091526040015f205560018316156129d0575f612915826122e0600187614599565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161297691600401614569565b602060405180830381865af4158015612991573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129b59190614418565b647fffffffff600195861c16949093509190910190506128d5565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0061111c565b612a0761309d565b611620816130c2565b602060ff82161115612a5e5760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b6044820152606401610806565b612a6f600160ff831681901b6147aa565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b60068101545f5b81811015612bb0575f612aaa82600161445b565b90505b82811015612ba7575f846006018381548110612acb57612acb6143ec565b5f9182526020822001546006870180546001600160a01b0390921693509084908110612af957612af96143ec565b5f918252602090912001546001600160a01b0390811691508216811015612b9d5780866006018581548110612b3057612b306143ec565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612b7157612b716143ec565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612aad565b50600101612a96565b505050565b5f8211612bd55760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612bfe576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612c34916147aa565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612c7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c9f9190614418565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cf2573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d169190614418565b90505f8111612d385760405163aeaddff160e01b815260040160405180910390fd5b5f612d4382846147bd565b90505f8111612d655760405163149fbcfd60e11b815260040160405180910390fd5b80861115611b715760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff1690811115612e0457508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612f80565b5f5f90505f876009015f855f81548110612e2057612e206143ec565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612ea8575f896009015f878481548110612e6a57612e6a6143ec565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e9f578092508193505b50600101612e49565b50808610612ebc575f945050505050612f80565b5f88600a015f868581548110612ed457612ed46143ec565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612f1157612f1161404d565b021790555086848381548110612f2957612f296143ec565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff1611612fda5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e20300000000000006044820152606401610806565b602060ff83161115612ffe5760405162461bcd60e51b8152600401610806906147dc565b8254600160281b900464ffffffffff168061301d60ff8516600261492d565b64ffffffffff16101561306d5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b6044820152606401610806565b6130788482856130ca565b949350505050565b5f8161309360ff851663ffffffff614946565b612f80919061454c565b6130a5613192565b61133c57604051631afcd79f60e31b815260040160405180910390fd5b61218961309d565b5f602060ff831611156130ef5760405162461bcd60e51b8152600401610806906147dc565b8264ffffffffff165f0361310d57613106826131ab565b9050612f80565b5f613119836001614533565b60ff166001600160401b0381111561313357613133614061565b60405190808252806020026020018201604052801561315c578160200160208202803683370190505b50905061316b85858584613845565b808360ff1681518110613180576131806143ec565b60200260200101519150509392505050565b5f61319b6129d7565b54600160401b900460ff16919050565b5f8160ff165f036131bd57505f919050565b8160ff166001036131ef57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361322157507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361325357507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361328557507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036132b757507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036132e957507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361331b57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361334d57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361337f57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036133b157507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036133e357507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361341557507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361344757507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361347957507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036134ab57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036134dd57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361350f57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361354157507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361357357507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036135a557507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036135d757507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361360957507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361363b57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361366d57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361369f57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036136d157507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361370357507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361373557507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361376757507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361379957507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f036137cb57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036137fd57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e64657800006044820152606401610806565b602060ff831611156138695760405162461bcd60e51b8152600401610806906147dc565b5f8364ffffffffff16116138cd5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b6064820152608401610806565b5f6138d9600185614599565b9050600181165f0361392c57846001015f6138f45f84613080565b64ffffffffff1681526020019081526020015f2054825f8151811061391b5761391b6143ec565b602002602001018181525050613954565b6139355f6131ab565b825f81518110613947576139476143ec565b6020026020010181815250505b5f5b8360ff168160ff16101561245557600182165f03613a4c5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff16815181106139a8576139a86143ec565b602002602001015181526020016139be856131ab565b8152506040518263ffffffff1660e01b81526004016139dd9190614569565b602060405180830381865af41580156139f8573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613a1c9190614418565b83613a28836001614533565b60ff1681518110613a3b57613a3b6143ec565b602002602001018181525050613bfd565b5f613a58826001614533565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613afa575f876001015f613aaf856001613a9e9190614533565b60018864ffffffffff16901c613080565b64ffffffffff1681526020019081526020015f205490508085846001613ad59190614533565b60ff1681518110613ae857613ae86143ec565b60200260200101818152505050613bfb565b5f876001015f613b11856001886122e09190614599565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613b6857613b686143ec565b60200260200101518152506040518263ffffffff1660e01b8152600401613b8f9190614569565b602060405180830381865af4158015613baa573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bce9190614418565b85613bda856001614533565b60ff1681518110613bed57613bed6143ec565b602002602001018181525050505b505b647fffffffff600192831c169101613956565b600183019183908215613ca1579160200282015f5b83821115613c6f57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613c25565b8015613c9f5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613c6f565b505b50613cad929150613cea565b5090565b828054828255905f5260205f20908101928215613ca1579160200282015b82811115613ca1578251825591602001919060010190613ccf565b5b80821115613cad575f8155600101613ceb565b6001600160a01b0381168114611620575f5ffd5b5f60208284031215613d22575f5ffd5b8135612f8081613cfe565b5f60208284031215613d3d575f5ffd5b5035919050565b5f5f83601f840112613d54575f5ffd5b5081356001600160401b03811115613d6a575f5ffd5b602083019150836020828501011115613d81575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f60a0898b031215613d9f575f5ffd5b8835975060208901356001600160401b03811115613dbb575f5ffd5b613dc78b828c01613d44565b9098509650506040890135945060608901356001600160401b03811115613dec575f5ffd5b613df88b828c01613d44565b90955093505060808901356001600160401b03811115613e16575f5ffd5b613e228b828c01613d44565b999c989b5096995094979396929594505050565b5f8151808452602084019350602083015f5b82811015613e6f5781516001600160a01b0316865260209586019590910190600101613e48565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613e6f578151865260209586019590910190600101613e8b565b604081525f613ebb6040830185613e36565b8281036020840152613ecd8185613e79565b95945050505050565b5f5f5f60808486031215613ee8575f5ffd5b833592506020840135915060808401851015613f02575f5ffd5b6040840190509250925092565b5f5f60408385031215613f20575f5ffd5b823591506020830135613f3281613cfe565b809150509250929050565b6001600160a01b0391909116815260200190565b5f5f5f60608486031215613f63575f5ffd5b833592506020840135613f7581613cfe565b929592945050506040919091013590565b602081525f612f806020830184613e36565b606081525f613faa6060830186613e79565b8281036020840152613fbc8186613e79565b90508281036040840152613fd08185613e79565b9695505050505050565b5f5f60408385031215613feb575f5ffd5b8235613ff681613cfe565b946020939093013593505050565b5f5f60408385031215614015575f5ffd5b50508035926020909101359150565b634e487b7160e01b5f52601160045260245ffd5b5f8161404657614046614024565b505f190190565b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b6040516101e081016001600160401b038111828210171561409857614098614061565b60405290565b604051601f8201601f191681016001600160401b03811182821017156140c6576140c6614061565b604052919050565b80516004811061216a575f5ffd5b5f82601f8301126140eb575f5ffd5b604080519081016001600160401b038111828210171561410d5761410d614061565b8060405250806040840185811115614123575f5ffd5b845b8181101561413d578051835260209283019201614125565b509195945050505050565b805161216a81613cfe565b805160ff8116811461216a575f5ffd5b5f82601f830112614172575f5ffd5b81516001600160401b0381111561418b5761418b614061565b61419e601f8201601f191660200161409e565b8181528460208386010111156141b2575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b8051801515811461216a575f5ffd5b5f602082840312156141ed575f5ffd5b81516001600160401b03811115614202575f5ffd5b82016102008185031215614214575f5ffd5b61421c614075565b8151815261422c602083016140ce565b60208201526040828101519082015261424885606084016140dc565b606082015260a0820151608082015261426360c08301614148565b60a082015261427460e08301614153565b60c08201526101008201516001600160401b03811115614292575f5ffd5b61429e86828501614163565b60e0830152506142b16101208301614148565b6101008201526142c46101408301614148565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b038111156142fa575f5ffd5b61430686828501614163565b6101808301525061431a6101c08301614148565b6101a082015261432d6101e083016141ce565b6101c0820152949350505050565b5f8154808452602084019350825f5260205f205f5b82811015613e6f5781546001600160a01b0316865260209095019460019182019101614350565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b608081525f6143b1608083018961433b565b82810360208401526143c481888a614377565b905085604084015282810360608401526143df818587614377565b9998505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f6001820161441157614411614024565b5060010190565b5f60208284031215614428575f5ffd5b5051919050565b803563ffffffff8116811461216a575f5ffd5b5f60208284031215614452575f5ffd5b612f808261442f565b8082018082111561111c5761111c614024565b84815260a0810160208201855f5b60028110156144a95763ffffffff6144938361442f565b168352602092830192919091019060010161447c565b50505060608201939093526080015292915050565b5f602082840312156144ce575f5ffd5b612f80826141ce565b604081525f613ebb604083018561433b565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff818116838216019081111561111c5761111c614024565b64ffffffffff818116838216019081111561111c5761111c614024565b6040810181835f5b6002811015614590578151835260209283019290910190600101614571565b50505092915050565b64ffffffffff828116828216039081111561111c5761111c614024565b808202811582820484141761111c5761111c614024565b848152836020820152606060408201525f613fd0606083018486614377565b60018060a01b038816815286602082015285604082015260a060608201525f61461960a083018688614377565b828103608084015261462c818587614377565b9a9950505050505050505050565b5f6001600160401b0382111561465257614652614061565b5060051b60200190565b5f82601f83011261466b575f5ffd5b815161467e6146798261463a565b61409e565b8082825260208201915060208360051b86010192508583111561469f575f5ffd5b602085015b838110156146bc5780518352602092830192016146a4565b5095945050505050565b5f5f5f606084860312156146d8575f5ffd5b83516001600160401b038111156146ed575f5ffd5b8401601f810186136146fd575f5ffd5b805161470b6146798261463a565b8082825260208201915060208360051b85010192508883111561472c575f5ffd5b6020840193505b8284101561474e578351825260209384019390910190614733565b8096505050505060208401516001600160401b0381111561476d575f5ffd5b6147798682870161465c565b92505060408401516001600160401b03811115614794575f5ffd5b6147a08682870161465c565b9150509250925092565b8181038181111561111c5761111c614024565b5f826147d757634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b60018411156114dd5780850481111561483e5761483e614024565b600184161561484c57908102905b60019390931c928002614823565b5f826148685750600161111c565b8161487457505f61111c565b816001811461488a5760028114614894576148c6565b600191505061111c565b60ff8411156148a5576148a5614024565b6001841b915064ffffffffff8211156148c0576148c0614024565b5061111c565b5060208310610133831016604e8410600b84101617156148fe575081810a64ffffffffff8111156148f9576148f9614024565b61111c565b61490e64ffffffffff848461481f565b8064ffffffffff0482111561492557614925614024565b029392505050565b5f612f8064ffffffffff841664ffffffffff841661485a565b64ffffffffff818116838216029081169081811461496657614966614024565b509291505056fea164736f6c634300081c000a", -======= - "bytecode": "0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b613dd7806100d96000396000f3fe608060405234801561001057600080fd5b50600436106102255760003560e01c806301ffc9a71461022a578063096b810a14610252578063099a161a146102675780630f3e34121461028857806317d611201461029b5780631e08d0e8146102bc5780632800d829146102c4578063291a691b146102d75780632e7b716d146102ea5780634d6861a6146102fd57806350e6d94c146103105780635d5047761461033357806370e36bbe14610346578063715018a61461035957806379ba5097146103615780637c92f5241461036957806385814243146103965780638a78bb15146103b65780638cb89ecb146103c95780638d1ddfb1146103e95780638da5cb5b146103ff5780638e5ce3ad146104075780639015d3711461041a5780639a7a2ffc1461042d5780639f0f874a1461046a578063a016493014610473578063a8a4d69b14610493578063bbe4b803146104a6578063bff232c1146104b0578063c2b40ae4146104c3578063c3a0ec30146104e3578063c6b2a438146104f4578063ca2869a014610507578063cd6dc68714610527578063cf90b6ed1461053a578063da881e5a14610544578063dbb06c9314610557578063e30c39781461056a578063e59e469514610572578063e6745e1314610585578063e82f3b7014610598578063ebf0c717146105ab578063f1650536146105b3578063f2fde38b146105cd578063f379b0df146105e0578063f52fd8031461061a578063f6fc05d51461068b575b600080fd5b61023d6102383660046132f8565b610694565b60405190151581526020015b60405180910390f35b610265610260366004613337565b6106cb565b005b61027a610275366004613354565b61080a565b604051908152602001610249565b610265610296366004613354565b610844565b6102ae6102a9366004613354565b6108be565b6040516102499291906133e4565b61027a600181565b61027a6102d2366004613354565b610a6e565b61023d6102e5366004613412565b610abb565b61023d6102f8366004613337565b610c9c565b61023d61030b366004613354565b610d4d565b61023d61031e366004613337565b60066020526000908152604090205460ff1681565b61023d61034136600461344f565b610d8e565b610265610354366004613337565b610dd3565b610265610e4a565b610265610e6e565b61037c61037736600461347f565b610ead565b6040805192835263ffffffff909116602083015201610249565b6001546103a9906001600160a01b031681565b60405161024991906134b7565b6102656103c4366004613337565b611058565b61027a6103d7366004613354565b60096020526000908152604090205481565b600454600160281b900464ffffffffff1661027a565b6103a96111a3565b600b546103a9906001600160a01b031681565b61023d610428366004613337565b6111be565b61045461043b366004613337565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff9091168152602001610249565b61027a60035481565b610486610481366004613354565b6111dc565b60405161024991906134cb565b61023d6104a136600461344f565b611275565b61027a6210000081565b6102656104be366004613337565b6112ba565b61027a6104d1366004613354565b60086020526000908152604090205481565b6001546001600160a01b03166103a9565b610265610502366004613526565b611333565b61027a610515366004613354565b60009081526008602052604090205490565b6102656105353660046135ac565b611624565b61027a62093a8081565b61023d610552366004613354565b611783565b6000546103a9906001600160a01b031681565b6103a9611a6f565b610265610580366004613337565b611a7a565b6102656105933660046135d8565b611af3565b61027a6105a6366004613354565b611cb5565b61027a611ce7565b6105bb601481565b60405160ff9091168152602001610249565b6102656105db366004613337565b611cfa565b6004546105fc9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff938416815292909116602083015201610249565b61065c610628366004613354565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b604051610249949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61027a60025481565b60006001600160e01b0319821663cb54661360e01b14806106c557506001600160e01b031982166301ffc9a760e01b145b92915050565b6106d36111a3565b6001600160a01b0316336001600160a01b031614806106fc57506001546001600160a01b031633145b61071957604051632864c4e160e01b815260040160405180910390fd5b610722816111be565b819061074b576040516381e5828960e01b815260040161074291906134b7565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff169061077a9060049083611d6b565b6001600160a01b0382166000908152600660205260408120805460ff1916905560028054916107a883613610565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5926107fe92869291600160281b900464ffffffffff1690613627565b60405180910390a25050565b6000818152600a60205260408120600481015461083a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61084c611fb7565b60018110158015610860575062093a808111155b81906108825760405163028237cd60e61b815260040161074291815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b038111156108f5576108f5613648565b60405190808252806020026020018201604052801561091e578160200160208202803683370190505b509450806001600160401b0381111561093957610939613648565b604051908082528060200260200182016040528015610962578160200160208202803683370190505b5093506000805b83811015610a645760008560060182815481106109885761098861365e565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff1660028111156109d0576109d0613674565b03610a5b57808884815181106109e8576109e861365e565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610a4257610a4261365e565b602090810291909101015282610a578161368a565b9350505b50600101610969565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610a9357610a93613674565b03610ab157604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610ae75760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff166003811115610b0c57610b0c613674565b14610b2a576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b9891906136a3565b905080610bab60408601602087016136d0565b63ffffffff161115610bc360408601602087016136d0565b829091610bf1576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610742565b5050815460ff19166001908117835582018590554260028301819055600354610c19916136eb565b6003830155610c2d60058301856002613241565b50610c36611ce7565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610c88928a928a92916136fe565b60405180910390a250600195945050505050565b6000610ca7826111be565b610cb357506000919050565b6001546001600160a01b0316610cdc576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610d0c9085906004016134b7565b602060405180830381865afa158015610d29573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c5919061375f565b6000818152600a602052604081206001815460ff166003811115610d7357610d73613674565b14610d815750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115610dcb57610dcb613674565b149392505050565b610ddb611fb7565b6001600160a01b038116610e025760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610e52611fb7565b6040516001623f026d60e01b0319815260040160405180910390fd5b3380610e78611a6f565b6001600160a01b031614610ea1578060405163118cdaa760e01b815260040161074291906134b7565b610eaa81611feb565b50565b600b5460009081906001600160a01b03163314610edd5760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff166003811115610f0357610f03613674565b14610f2157604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff166002811115610f6257610f62613674565b14610f7257600b01549150611050565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b8201805491610fa783613610565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610ff8929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6110606111a3565b6001600160a01b0316336001600160a01b0316148061108957506001546001600160a01b031633145b6110a657604051632864c4e160e01b815260040160405180910390fd5b6110af816111be565b610eaa57600454600160281b900464ffffffffff166210000081106110e7576040516335b4ac3f60e01b815260040160405180910390fd5b6110fb60046001600160a01b038416612012565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161114d8361368a565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53926107fe92869291600160281b900464ffffffffff1690613627565b6000806111ae61218d565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a60205260409020600481015460609190611210576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561126857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161124a575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156112b1576112b1613674565b14159392505050565b6112c2611fb7565b6001600160a01b0381166112e95760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b6000868152600a602052604090206002815460ff16600381111561135957611359613674565b1461137757604051634f4b461f60e11b815260040160405180910390fd5b60048101541561139a5760405163632a22bb60e01b815260040160405180910390fd5b836113b857604051636caad1ed60e11b815260040160405180910390fd5b600061141f8260060180548060200260200160405190810160405280929190818152602001828054801561141557602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113f7575b50505050506121b1565b60078301819055600480840187905560008a8152600960205260408082208990558154905163101bb4d760e21b81529283018c9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015611488573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114b091908101906138f2565b9050806101c001511561156f57836114db57604051630fb0193f60e41b815260040160405180910390fd5b61012081015160008a815260086020526040908190205490516303a0d4ed60e11b81526001600160a01b0390921691630741a9da9161152c918d919060068901908c9089908d908d90600401613abf565b602060405180830381865afa158015611549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156d919061375f565b505b6000546040516340a3b76160e11b8152600481018b9052602481018890526001600160a01b03909116906381476ec290604401600060405180830381600087803b1580156115bc57600080fd5b505af11580156115d0573d6000803e3d6000fd5b50505050887fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018a8a8a8a8a60405161161196959493929190613b0b565b60405180910390a2505050505050505050565b600061162e6121e1565b805490915060ff600160401b82041615906001600160401b03166000811580156116555750825b90506000826001600160401b031660011480156116715750303b155b90508115801561167f575080155b1561169d5760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b031916600117855583156116c657845460ff60401b1916600160401b1785555b6001600160a01b0387166116ed5760405163d92e233d60e01b815260040160405180910390fd5b6116f63361220a565b6117026004601461221b565b61170b86610844565b6117136111a3565b6001600160a01b0316876001600160a01b0316146117345761173487611feb565b831561177a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff1660038111156117a8576117a8613674565b036117c657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156117de576117de613674565b146117fc57604051631860f69960e31b815260040160405180910390fd5b8060030154421161182057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061190c578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b1580156118ea57600080fd5b505af11580156118fe573d6000803e3d6000fd5b506000979650505050505050565b815460ff191660021782556006820154600b83018190556000816001600160401b0381111561193d5761193d613648565b604051908082528060200260200182016040528015611966578160200160208202803683370190505b50905060005b828110156119db5784600901600086600601838154811061198f5761198f61365e565b60009182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106119c8576119c861365e565b602090810291909101015260010161196c565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611a2257600080fd5b505af1158015611a36573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e58560060183604051610c88929190613b59565b6000806111ae61225a565b611a82611fb7565b6001600160a01b038116611aa95760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff166003811115611b1857611b18613674565b03611b3657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611b4e57611b4e613674565b14611b6c57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611b9157604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff1615611bc45760405163257309f160e11b815260040160405180910390fd5b611bcd33610c9c565b611bea5760405163149fbcfd60e11b815260040160405180910390fd5b611bf533838561227e565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff19166001179055909150611c719083908361245a565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b60008181526009602052604090205480611ce2576040516322e679e360e11b815260040160405180910390fd5b919050565b6000611cf560046014612666565b905090565b611d02611fb7565b6000611d0c61225a565b80546001600160a01b0319166001600160a01b0384169081178255909150611d326111a3565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020613dab8339815191528210611d8557600080fd5b825464ffffffffff600160281b90910481169082168111611da557600080fd5b8260005b81866001016000611dba84886126cc565b64ffffffffff168152602001908152602001600020819055506000816001611de29190613b6c565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611e175750611faf565b60018516600003611ee3576000611e3883611e33886001613b85565b6126cc565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e9a91600401613ba2565b602060405180830381865af4158015611eb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611edb91906136a3565b935050611f9b565b6000611ef483611e33600189613bd3565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f5691600401613ba2565b602060405180830381865af4158015611f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9791906136a3565b9350505b50647fffffffff600194851c169301611da9565b505050505050565b33611fc06111a3565b6001600160a01b031614611fe9573360405163118cdaa760e01b815260040161074291906134b7565b565b6000611ff561225a565b80546001600160a01b0319168155905061200e826126ea565b5050565b8154600160281b900464ffffffffff16600080516020613dab833981519152821061203c57600080fd5b825464ffffffffff9081169082161061205457600080fd5b61205f816001613b85565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b8185600101600061209684876126cc565b64ffffffffff16815260208101919091526040016000205560018316156121865760006120c882611e33600187613bd3565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161212a91600401613ba2565b602060405180830381865af4158015612147573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216b91906136a3565b647fffffffff600195861c1694909350919091019050612085565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000816040516020016121c49190613bf0565b604051602081830303815290604052805190602001209050919050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006106c5565b612212612746565b610eaa8161276b565b602060ff8216111561222c57600080fd5b61223d600160ff831681901b613c24565b82546001600160501b03191664ffffffffff919091161790915550565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b6000821161229f5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166122c8576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd719188916122ff91613c24565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612348573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236c91906136a3565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e791906136a3565b90506000811161240a5760405163aeaddff160e01b815260040160405180910390fd5b60006124168284613c37565b9050600081116124395760405163149fbcfd60e11b815260040160405180910390fd5b8086111561177a5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff16908111156124da57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff191682179055905061265f565b600080876009016000856000815481106124f6576124f661365e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b84548110156125825760008960090160008784815481106125435761254361365e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612579578092508193505b50600101612520565b5080861061259757600094505050505061265f565b600088600a0160008685815481106125b1576125b161365e565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125ef576125ef613674565b0217905550868483815481106126075761260761365e565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff161161267757600080fd5b602060ff8316111561268857600080fd5b8254600160281b900464ffffffffff16806126a760ff85166002613d69565b64ffffffffff1610156126b957600080fd5b6126c484828561279d565b949350505050565b6000816126e060ff851663ffffffff613d83565b61265f9190613b85565b60006126f461218d565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b61274e612855565b611fe957604051631afcd79f60e31b815260040160405180910390fd5b612773612746565b6001600160a01b038116610ea1576000604051631e4fbdf760e01b815260040161074291906134b7565b6000602060ff831611156127b057600080fd5b8264ffffffffff166000036127cf576127c88261286f565b905061265f565b60006127dc836001613b6c565b60ff166001600160401b038111156127f6576127f6613648565b60405190808252806020026020018201604052801561281f578160200160208202803683370190505b50905061282e85858584612ec4565b808360ff16815181106128435761284361365e565b60200260200101519150509392505050565b600061285f6121e1565b54600160401b900460ff16919050565b60008160ff1660000361288457506000919050565b8160ff166001036128b657507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036128e857507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361291a57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361294c57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361297e57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036129b057507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036129e257507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612a1457507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612a4657507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612a7857507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612aaa57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612adc57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612b0e57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612b4057507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612b7257507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612ba457507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612bd657507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612c0857507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612c3a57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612c6c57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612c9e57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612cd057507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612d0257507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612d3457507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612d6657507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612d9857507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612dca57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612dfc57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612e2e57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612e6057507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612e9257507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361022557507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff83161115612ed557600080fd5b60008364ffffffffff1611612ee957600080fd5b6000612ef6600185613bd3565b905060018116600003612f4e57846001016000612f146000846126cc565b64ffffffffff1681526020019081526020016000205482600081518110612f3d57612f3d61365e565b602002602001018181525050612f78565b612f58600061286f565b82600081518110612f6b57612f6b61365e565b6020026020010181815250505b60005b8360ff168160ff161015611faf57600182166000036130745773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612fce57612fce61365e565b60200260200101518152602001612fe48561286f565b8152506040518263ffffffff1660e01b81526004016130039190613ba2565b602060405180830381865af4158015613020573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304491906136a3565b83613050836001613b6c565b60ff16815181106130635761306361365e565b60200260200101818152505061322e565b6000613081826001613b6c565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156131265760008760010160006130da8560016130c99190613b6c565b60018864ffffffffff16901c6126cc565b64ffffffffff16815260200190815260200160002054905080858460016131019190613b6c565b60ff16815181106131145761311461365e565b6020026020010181815250505061322c565b600087600101600061313f85600188611e339190613bd3565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106131975761319761365e565b60200260200101518152506040518263ffffffff1660e01b81526004016131be9190613ba2565b602060405180830381865af41580156131db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ff91906136a3565b8561320b856001613b6c565b60ff168151811061321e5761321e61365e565b602002602001018181525050505b505b647fffffffff600192831c169101612f7b565b6001830191839082156132d35791602002820160005b838211156132a157833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613257565b80156132d15782816101000a81549063ffffffff02191690556004016020816003010492830192600103026132a1565b505b506132df9291506132e3565b5090565b5b808211156132df57600081556001016132e4565b60006020828403121561330a57600080fd5b81356001600160e01b03198116811461265f57600080fd5b6001600160a01b0381168114610eaa57600080fd5b60006020828403121561334957600080fd5b813561265f81613322565b60006020828403121561336657600080fd5b5035919050565b600081518084526020840193506020830160005b828110156133a85781516001600160a01b0316865260209586019590910190600101613381565b5093949350505050565b600081518084526020840193506020830160005b828110156133a85781518652602095860195909101906001016133c6565b6040815260006133f7604083018561336d565b828103602084015261340981856133b2565b95945050505050565b60008060006080848603121561342757600080fd5b83359250602084013591506080840185101561344257600080fd5b6040840190509250925092565b6000806040838503121561346257600080fd5b82359150602083013561347481613322565b809150509250929050565b60008060006060848603121561349457600080fd5b8335925060208401356134a681613322565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b60208152600061265f602083018461336d565b60008083601f8401126134f057600080fd5b5081356001600160401b0381111561350757600080fd5b60208301915083602082850101111561351f57600080fd5b9250929050565b6000806000806000806080878903121561353f57600080fd5b8635955060208701356001600160401b0381111561355c57600080fd5b61356889828a016134de565b9096509450506040870135925060608701356001600160401b0381111561358e57600080fd5b61359a89828a016134de565b979a9699509497509295939492505050565b600080604083850312156135bf57600080fd5b82356135ca81613322565b946020939093013593505050565b600080604083850312156135eb57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161361f5761361f6135fa565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b60006001820161369c5761369c6135fa565b5060010190565b6000602082840312156136b557600080fd5b5051919050565b803563ffffffff81168114611ce257600080fd5b6000602082840312156136e257600080fd5b61265f826136bc565b808201808211156106c5576106c56135fa565b84815260a08101602082018560005b600281101561373a5763ffffffff613724836136bc565b168352602092830192919091019060010161370d565b50505060608201939093526080015292915050565b80518015158114611ce257600080fd5b60006020828403121561377157600080fd5b61265f8261374f565b6040516101e081016001600160401b038111828210171561379d5761379d613648565b60405290565b604051601f8201601f191681016001600160401b03811182821017156137cb576137cb613648565b604052919050565b805160048110611ce257600080fd5b600082601f8301126137f357600080fd5b604080519081016001600160401b038111828210171561381557613815613648565b806040525080604084018581111561382c57600080fd5b845b8181101561384657805183526020928301920161382e565b509195945050505050565b8051611ce281613322565b805160ff81168114611ce257600080fd5b600082601f83011261387e57600080fd5b81516001600160401b0381111561389757613897613648565b6138aa601f8201601f19166020016137a3565b8181528460208386010111156138bf57600080fd5b60005b828110156138de576020818601810151838301820152016138c2565b506000918101602001919091529392505050565b60006020828403121561390457600080fd5b81516001600160401b0381111561391a57600080fd5b8201610200818503121561392d57600080fd5b61393561377a565b81518152613945602083016137d3565b60208201526040828101519082015261396185606084016137e2565b606082015260a0820151608082015261397c60c08301613851565b60a082015261398d60e0830161385c565b60c08201526101008201516001600160401b038111156139ac57600080fd5b6139b88682850161386d565b60e0830152506139cb6101208301613851565b6101008201526139de6101408301613851565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a1557600080fd5b613a218682850161386d565b61018083015250613a356101c08301613851565b6101a0820152613a486101e0830161374f565b6101c0820152949350505050565b6000815480845260208401935082600052602060002060005b828110156133a85781546001600160a01b0316865260209095019460019182019101613a6f565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b87815286602082015260c060408201526000613ade60c0830188613a56565b86606084015285608084015282810360a0840152613afd818587613a96565b9a9950505050505050505050565b608081526000613b1e6080830189613a56565b8281036020840152613b3181888a613a96565b90508560408401528281036060840152613b4c818587613a96565b9998505050505050505050565b6040815260006133f76040830185613a56565b60ff81811683821601908111156106c5576106c56135fa565b64ffffffffff81811683821601908111156106c5576106c56135fa565b60408101818360005b6002811015613bca578151835260209283019290910190600101613bab565b50505092915050565b64ffffffffff82811682821603908111156106c5576106c56135fa565b8151600090829060208501835b828110156138465781516001600160a01b0316845260209384019390910190600101613bfd565b818103818111156106c5576106c56135fa565b600082613c5457634e487b7160e01b600052601260045260246000fd5b500490565b6001815b600184111561105057808504811115613c7857613c786135fa565b6001841615613c8657908102905b60019390931c928002613c5d565b600082613ca3575060016106c5565b81613cb0575060006106c5565b8160018114613cc65760028114613cd057613d02565b60019150506106c5565b60ff841115613ce157613ce16135fa565b6001841b915064ffffffffff821115613cfc57613cfc6135fa565b506106c5565b5060208310610133831016604e8410600b8410161715613d3a575081810a64ffffffffff811115613d3557613d356135fa565b6106c5565b613d4a64ffffffffff8484613c59565b8064ffffffffff04821115613d6157613d616135fa565b029392505050565b600061265f64ffffffffff841664ffffffffff8416613c94565b64ffffffffff8181168382160290811690818114613da357613da36135fa565b509291505056fe30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102255760003560e01c806301ffc9a71461022a578063096b810a14610252578063099a161a146102675780630f3e34121461028857806317d611201461029b5780631e08d0e8146102bc5780632800d829146102c4578063291a691b146102d75780632e7b716d146102ea5780634d6861a6146102fd57806350e6d94c146103105780635d5047761461033357806370e36bbe14610346578063715018a61461035957806379ba5097146103615780637c92f5241461036957806385814243146103965780638a78bb15146103b65780638cb89ecb146103c95780638d1ddfb1146103e95780638da5cb5b146103ff5780638e5ce3ad146104075780639015d3711461041a5780639a7a2ffc1461042d5780639f0f874a1461046a578063a016493014610473578063a8a4d69b14610493578063bbe4b803146104a6578063bff232c1146104b0578063c2b40ae4146104c3578063c3a0ec30146104e3578063c6b2a438146104f4578063ca2869a014610507578063cd6dc68714610527578063cf90b6ed1461053a578063da881e5a14610544578063dbb06c9314610557578063e30c39781461056a578063e59e469514610572578063e6745e1314610585578063e82f3b7014610598578063ebf0c717146105ab578063f1650536146105b3578063f2fde38b146105cd578063f379b0df146105e0578063f52fd8031461061a578063f6fc05d51461068b575b600080fd5b61023d6102383660046132f8565b610694565b60405190151581526020015b60405180910390f35b610265610260366004613337565b6106cb565b005b61027a610275366004613354565b61080a565b604051908152602001610249565b610265610296366004613354565b610844565b6102ae6102a9366004613354565b6108be565b6040516102499291906133e4565b61027a600181565b61027a6102d2366004613354565b610a6e565b61023d6102e5366004613412565b610abb565b61023d6102f8366004613337565b610c9c565b61023d61030b366004613354565b610d4d565b61023d61031e366004613337565b60066020526000908152604090205460ff1681565b61023d61034136600461344f565b610d8e565b610265610354366004613337565b610dd3565b610265610e4a565b610265610e6e565b61037c61037736600461347f565b610ead565b6040805192835263ffffffff909116602083015201610249565b6001546103a9906001600160a01b031681565b60405161024991906134b7565b6102656103c4366004613337565b611058565b61027a6103d7366004613354565b60096020526000908152604090205481565b600454600160281b900464ffffffffff1661027a565b6103a96111a3565b600b546103a9906001600160a01b031681565b61023d610428366004613337565b6111be565b61045461043b366004613337565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff9091168152602001610249565b61027a60035481565b610486610481366004613354565b6111dc565b60405161024991906134cb565b61023d6104a136600461344f565b611275565b61027a6210000081565b6102656104be366004613337565b6112ba565b61027a6104d1366004613354565b60086020526000908152604090205481565b6001546001600160a01b03166103a9565b610265610502366004613526565b611333565b61027a610515366004613354565b60009081526008602052604090205490565b6102656105353660046135ac565b611624565b61027a62093a8081565b61023d610552366004613354565b611783565b6000546103a9906001600160a01b031681565b6103a9611a6f565b610265610580366004613337565b611a7a565b6102656105933660046135d8565b611af3565b61027a6105a6366004613354565b611cb5565b61027a611ce7565b6105bb601481565b60405160ff9091168152602001610249565b6102656105db366004613337565b611cfa565b6004546105fc9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff938416815292909116602083015201610249565b61065c610628366004613354565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b604051610249949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61027a60025481565b60006001600160e01b0319821663cb54661360e01b14806106c557506001600160e01b031982166301ffc9a760e01b145b92915050565b6106d36111a3565b6001600160a01b0316336001600160a01b031614806106fc57506001546001600160a01b031633145b61071957604051632864c4e160e01b815260040160405180910390fd5b610722816111be565b819061074b576040516381e5828960e01b815260040161074291906134b7565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff169061077a9060049083611d6b565b6001600160a01b0382166000908152600660205260408120805460ff1916905560028054916107a883613610565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5926107fe92869291600160281b900464ffffffffff1690613627565b60405180910390a25050565b6000818152600a60205260408120600481015461083a576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61084c611fb7565b60018110158015610860575062093a808111155b81906108825760405163028237cd60e61b815260040161074291815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b038111156108f5576108f5613648565b60405190808252806020026020018201604052801561091e578160200160208202803683370190505b509450806001600160401b0381111561093957610939613648565b604051908082528060200260200182016040528015610962578160200160208202803683370190505b5093506000805b83811015610a645760008560060182815481106109885761098861365e565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff1660028111156109d0576109d0613674565b03610a5b57808884815181106109e8576109e861365e565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610a4257610a4261365e565b602090810291909101015282610a578161368a565b9350505b50600101610969565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610a9357610a93613674565b03610ab157604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610ae75760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff166003811115610b0c57610b0c613674565b14610b2a576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b9891906136a3565b905080610bab60408601602087016136d0565b63ffffffff161115610bc360408601602087016136d0565b829091610bf1576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610742565b5050815460ff19166001908117835582018590554260028301819055600354610c19916136eb565b6003830155610c2d60058301856002613241565b50610c36611ce7565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610c88928a928a92916136fe565b60405180910390a250600195945050505050565b6000610ca7826111be565b610cb357506000919050565b6001546001600160a01b0316610cdc576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610d0c9085906004016134b7565b602060405180830381865afa158015610d29573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c5919061375f565b6000818152600a602052604081206001815460ff166003811115610d7357610d73613674565b14610d815750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115610dcb57610dcb613674565b149392505050565b610ddb611fb7565b6001600160a01b038116610e025760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610e52611fb7565b6040516001623f026d60e01b0319815260040160405180910390fd5b3380610e78611a6f565b6001600160a01b031614610ea1578060405163118cdaa760e01b815260040161074291906134b7565b610eaa81611feb565b50565b600b5460009081906001600160a01b03163314610edd5760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff166003811115610f0357610f03613674565b14610f2157604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff166002811115610f6257610f62613674565b14610f7257600b01549150611050565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b8201805491610fa783613610565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610ff8929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6110606111a3565b6001600160a01b0316336001600160a01b0316148061108957506001546001600160a01b031633145b6110a657604051632864c4e160e01b815260040160405180910390fd5b6110af816111be565b610eaa57600454600160281b900464ffffffffff166210000081106110e7576040516335b4ac3f60e01b815260040160405180910390fd5b6110fb60046001600160a01b038416612012565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff19909116179055600280549161114d8361368a565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53926107fe92869291600160281b900464ffffffffff1690613627565b6000806111ae61218d565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a60205260409020600481015460609190611210576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561126857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161124a575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156112b1576112b1613674565b14159392505050565b6112c2611fb7565b6001600160a01b0381166112e95760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b6000868152600a602052604090206002815460ff16600381111561135957611359613674565b1461137757604051634f4b461f60e11b815260040160405180910390fd5b60048101541561139a5760405163632a22bb60e01b815260040160405180910390fd5b836113b857604051636caad1ed60e11b815260040160405180910390fd5b600061141f8260060180548060200260200160405190810160405280929190818152602001828054801561141557602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113f7575b50505050506121b1565b60078301819055600480840187905560008a8152600960205260408082208990558154905163101bb4d760e21b81529283018c9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015611488573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114b091908101906138f2565b9050806101c001511561156f57836114db57604051630fb0193f60e41b815260040160405180910390fd5b61012081015160008a815260086020526040908190205490516303a0d4ed60e11b81526001600160a01b0390921691630741a9da9161152c918d919060068901908c9089908d908d90600401613abf565b602060405180830381865afa158015611549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156d919061375f565b505b6000546040516340a3b76160e11b8152600481018b9052602481018890526001600160a01b03909116906381476ec290604401600060405180830381600087803b1580156115bc57600080fd5b505af11580156115d0573d6000803e3d6000fd5b50505050887fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018a8a8a8a8a60405161161196959493929190613b0b565b60405180910390a2505050505050505050565b600061162e6121e1565b805490915060ff600160401b82041615906001600160401b03166000811580156116555750825b90506000826001600160401b031660011480156116715750303b155b90508115801561167f575080155b1561169d5760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b031916600117855583156116c657845460ff60401b1916600160401b1785555b6001600160a01b0387166116ed5760405163d92e233d60e01b815260040160405180910390fd5b6116f63361220a565b6117026004601461221b565b61170b86610844565b6117136111a3565b6001600160a01b0316876001600160a01b0316146117345761173487611feb565b831561177a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff1660038111156117a8576117a8613674565b036117c657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156117de576117de613674565b146117fc57604051631860f69960e31b815260040160405180910390fd5b8060030154421161182057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061190c578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b1580156118ea57600080fd5b505af11580156118fe573d6000803e3d6000fd5b506000979650505050505050565b815460ff191660021782556006820154600b83018190556000816001600160401b0381111561193d5761193d613648565b604051908082528060200260200182016040528015611966578160200160208202803683370190505b50905060005b828110156119db5784600901600086600601838154811061198f5761198f61365e565b60009182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106119c8576119c861365e565b602090810291909101015260010161196c565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611a2257600080fd5b505af1158015611a36573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e58560060183604051610c88929190613b59565b6000806111ae61225a565b611a82611fb7565b6001600160a01b038116611aa95760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff166003811115611b1857611b18613674565b03611b3657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611b4e57611b4e613674565b14611b6c57604051631860f69960e31b815260040160405180910390fd5b8060030154421115611b9157604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff1615611bc45760405163257309f160e11b815260040160405180910390fd5b611bcd33610c9c565b611bea5760405163149fbcfd60e11b815260040160405180910390fd5b611bf533838561227e565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff19166001179055909150611c719083908361245a565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b60008181526009602052604090205480611ce2576040516322e679e360e11b815260040160405180910390fd5b919050565b6000611cf560046014612666565b905090565b611d02611fb7565b6000611d0c61225a565b80546001600160a01b0319166001600160a01b0384169081178255909150611d326111a3565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020613dab8339815191528210611d8557600080fd5b825464ffffffffff600160281b90910481169082168111611da557600080fd5b8260005b81866001016000611dba84886126cc565b64ffffffffff168152602001908152602001600020819055506000816001611de29190613b6c565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611e175750611faf565b60018516600003611ee3576000611e3883611e33886001613b85565b6126cc565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611e9a91600401613ba2565b602060405180830381865af4158015611eb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611edb91906136a3565b935050611f9b565b6000611ef483611e33600189613bd3565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611f5691600401613ba2565b602060405180830381865af4158015611f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9791906136a3565b9350505b50647fffffffff600194851c169301611da9565b505050505050565b33611fc06111a3565b6001600160a01b031614611fe9573360405163118cdaa760e01b815260040161074291906134b7565b565b6000611ff561225a565b80546001600160a01b0319168155905061200e826126ea565b5050565b8154600160281b900464ffffffffff16600080516020613dab833981519152821061203c57600080fd5b825464ffffffffff9081169082161061205457600080fd5b61205f816001613b85565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b8185600101600061209684876126cc565b64ffffffffff16815260208101919091526040016000205560018316156121865760006120c882611e33600187613bd3565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161212a91600401613ba2565b602060405180830381865af4158015612147573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216b91906136a3565b647fffffffff600195861c1694909350919091019050612085565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000816040516020016121c49190613bf0565b604051602081830303815290604052805190602001209050919050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006106c5565b612212612746565b610eaa8161276b565b602060ff8216111561222c57600080fd5b61223d600160ff831681901b613c24565b82546001600160501b03191664ffffffffff919091161790915550565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b6000821161229f5760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166122c8576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd719188916122ff91613c24565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612348573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236c91906136a3565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123e791906136a3565b90506000811161240a5760405163aeaddff160e01b815260040160405180910390fd5b60006124168284613c37565b9050600081116124395760405163149fbcfd60e11b815260040160405180910390fd5b8086111561177a5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff16908111156124da57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff191682179055905061265f565b600080876009016000856000815481106124f6576124f661365e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b84548110156125825760008960090160008784815481106125435761254361365e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612579578092508193505b50600101612520565b5080861061259757600094505050505061265f565b600088600a0160008685815481106125b1576125b161365e565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156125ef576125ef613674565b0217905550868483815481106126075761260761365e565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff161161267757600080fd5b602060ff8316111561268857600080fd5b8254600160281b900464ffffffffff16806126a760ff85166002613d69565b64ffffffffff1610156126b957600080fd5b6126c484828561279d565b949350505050565b6000816126e060ff851663ffffffff613d83565b61265f9190613b85565b60006126f461218d565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b61274e612855565b611fe957604051631afcd79f60e31b815260040160405180910390fd5b612773612746565b6001600160a01b038116610ea1576000604051631e4fbdf760e01b815260040161074291906134b7565b6000602060ff831611156127b057600080fd5b8264ffffffffff166000036127cf576127c88261286f565b905061265f565b60006127dc836001613b6c565b60ff166001600160401b038111156127f6576127f6613648565b60405190808252806020026020018201604052801561281f578160200160208202803683370190505b50905061282e85858584612ec4565b808360ff16815181106128435761284361365e565b60200260200101519150509392505050565b600061285f6121e1565b54600160401b900460ff16919050565b60008160ff1660000361288457506000919050565b8160ff166001036128b657507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036128e857507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361291a57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361294c57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361297e57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036129b057507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036129e257507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff16600803612a1457507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff16600903612a4657507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a03612a7857507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b03612aaa57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c03612adc57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612b0e57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612b4057507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612b7257507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612ba457507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612bd657507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612c0857507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612c3a57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612c6c57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612c9e57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612cd057507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612d0257507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612d3457507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612d6657507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612d9857507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612dca57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612dfc57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612e2e57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612e6057507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612e9257507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361022557507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff83161115612ed557600080fd5b60008364ffffffffff1611612ee957600080fd5b6000612ef6600185613bd3565b905060018116600003612f4e57846001016000612f146000846126cc565b64ffffffffff1681526020019081526020016000205482600081518110612f3d57612f3d61365e565b602002602001018181525050612f78565b612f58600061286f565b82600081518110612f6b57612f6b61365e565b6020026020010181815250505b60005b8360ff168160ff161015611faf57600182166000036130745773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612fce57612fce61365e565b60200260200101518152602001612fe48561286f565b8152506040518263ffffffff1660e01b81526004016130039190613ba2565b602060405180830381865af4158015613020573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304491906136a3565b83613050836001613b6c565b60ff16815181106130635761306361365e565b60200260200101818152505061322e565b6000613081826001613b6c565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156131265760008760010160006130da8560016130c99190613b6c565b60018864ffffffffff16901c6126cc565b64ffffffffff16815260200190815260200160002054905080858460016131019190613b6c565b60ff16815181106131145761311461365e565b6020026020010181815250505061322c565b600087600101600061313f85600188611e339190613bd3565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106131975761319761365e565b60200260200101518152506040518263ffffffff1660e01b81526004016131be9190613ba2565b602060405180830381865af41580156131db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ff91906136a3565b8561320b856001613b6c565b60ff168151811061321e5761321e61365e565b602002602001018181525050505b505b647fffffffff600192831c169101612f7b565b6001830191839082156132d35791602002820160005b838211156132a157833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613257565b80156132d15782816101000a81549063ffffffff02191690556004016020816003010492830192600103026132a1565b505b506132df9291506132e3565b5090565b5b808211156132df57600081556001016132e4565b60006020828403121561330a57600080fd5b81356001600160e01b03198116811461265f57600080fd5b6001600160a01b0381168114610eaa57600080fd5b60006020828403121561334957600080fd5b813561265f81613322565b60006020828403121561336657600080fd5b5035919050565b600081518084526020840193506020830160005b828110156133a85781516001600160a01b0316865260209586019590910190600101613381565b5093949350505050565b600081518084526020840193506020830160005b828110156133a85781518652602095860195909101906001016133c6565b6040815260006133f7604083018561336d565b828103602084015261340981856133b2565b95945050505050565b60008060006080848603121561342757600080fd5b83359250602084013591506080840185101561344257600080fd5b6040840190509250925092565b6000806040838503121561346257600080fd5b82359150602083013561347481613322565b809150509250929050565b60008060006060848603121561349457600080fd5b8335925060208401356134a681613322565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b60208152600061265f602083018461336d565b60008083601f8401126134f057600080fd5b5081356001600160401b0381111561350757600080fd5b60208301915083602082850101111561351f57600080fd5b9250929050565b6000806000806000806080878903121561353f57600080fd5b8635955060208701356001600160401b0381111561355c57600080fd5b61356889828a016134de565b9096509450506040870135925060608701356001600160401b0381111561358e57600080fd5b61359a89828a016134de565b979a9699509497509295939492505050565b600080604083850312156135bf57600080fd5b82356135ca81613322565b946020939093013593505050565b600080604083850312156135eb57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161361f5761361f6135fa565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b60006001820161369c5761369c6135fa565b5060010190565b6000602082840312156136b557600080fd5b5051919050565b803563ffffffff81168114611ce257600080fd5b6000602082840312156136e257600080fd5b61265f826136bc565b808201808211156106c5576106c56135fa565b84815260a08101602082018560005b600281101561373a5763ffffffff613724836136bc565b168352602092830192919091019060010161370d565b50505060608201939093526080015292915050565b80518015158114611ce257600080fd5b60006020828403121561377157600080fd5b61265f8261374f565b6040516101e081016001600160401b038111828210171561379d5761379d613648565b60405290565b604051601f8201601f191681016001600160401b03811182821017156137cb576137cb613648565b604052919050565b805160048110611ce257600080fd5b600082601f8301126137f357600080fd5b604080519081016001600160401b038111828210171561381557613815613648565b806040525080604084018581111561382c57600080fd5b845b8181101561384657805183526020928301920161382e565b509195945050505050565b8051611ce281613322565b805160ff81168114611ce257600080fd5b600082601f83011261387e57600080fd5b81516001600160401b0381111561389757613897613648565b6138aa601f8201601f19166020016137a3565b8181528460208386010111156138bf57600080fd5b60005b828110156138de576020818601810151838301820152016138c2565b506000918101602001919091529392505050565b60006020828403121561390457600080fd5b81516001600160401b0381111561391a57600080fd5b8201610200818503121561392d57600080fd5b61393561377a565b81518152613945602083016137d3565b60208201526040828101519082015261396185606084016137e2565b606082015260a0820151608082015261397c60c08301613851565b60a082015261398d60e0830161385c565b60c08201526101008201516001600160401b038111156139ac57600080fd5b6139b88682850161386d565b60e0830152506139cb6101208301613851565b6101008201526139de6101408301613851565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a1557600080fd5b613a218682850161386d565b61018083015250613a356101c08301613851565b6101a0820152613a486101e0830161374f565b6101c0820152949350505050565b6000815480845260208401935082600052602060002060005b828110156133a85781546001600160a01b0316865260209095019460019182019101613a6f565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b87815286602082015260c060408201526000613ade60c0830188613a56565b86606084015285608084015282810360a0840152613afd818587613a96565b9a9950505050505050505050565b608081526000613b1e6080830189613a56565b8281036020840152613b3181888a613a96565b90508560408401528281036060840152613b4c818587613a96565b9998505050505050505050565b6040815260006133f76040830185613a56565b60ff81811683821601908111156106c5576106c56135fa565b64ffffffffff81811683821601908111156106c5576106c56135fa565b60408101818360005b6002811015613bca578151835260209283019290910190600101613bab565b50505092915050565b64ffffffffff82811682821603908111156106c5576106c56135fa565b8151600090829060208501835b828110156138465781516001600160a01b0316845260209384019390910190600101613bfd565b818103818111156106c5576106c56135fa565b600082613c5457634e487b7160e01b600052601260045260246000fd5b500490565b6001815b600184111561105057808504811115613c7857613c786135fa565b6001841615613c8657908102905b60019390931c928002613c5d565b600082613ca3575060016106c5565b81613cb0575060006106c5565b8160018114613cc65760028114613cd057613d02565b60019150506106c5565b60ff841115613ce157613ce16135fa565b6001841b915064ffffffffff821115613cfc57613cfc6135fa565b506106c5565b5060208310610133831016604e8410600b8410161715613d3a575081810a64ffffffffff811115613d3557613d356135fa565b6106c5565b613d4a64ffffffffff8484613c59565b8064ffffffffff04821115613d6157613d616135fa565b029392505050565b600061265f64ffffffffff841664ffffffffff8416613c94565b64ffffffffff8181168382160290811690818114613da357613da36135fa565b509291505056fe30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", ->>>>>>> main + "bytecode": "0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b614a40806100d96000396000f3fe608060405234801561001057600080fd5b50600436106102935760003560e01c806301ffc9a714610298578063096b810a146102c0578063099a161a146102d55780630b45f8e9146102f65780630bbfade7146103095780630f3e34121461031c57806317d611201461032f5780631e08d0e8146103505780632800d82914610358578063291a691b1461036b5780632e7b716d1461037e578063323beaa5146103915780634d6861a6146103a457806350e6d94c146103b757806350e6fad3146103da5780635d504776146103e457806362cc89a8146103f757806370e36bbe14610417578063715018a61461042a57806379ba5097146104325780637c92f5241461043a57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ad5780638da5cb5b146104c35780638e5ce3ad146104cb5780639015d371146104de5780639a7a2ffc146104f15780639f0f874a1461052e578063a016493014610537578063a8a4d69b14610557578063acc524941461056a578063b2d5d1ac1461057d578063b8ab470414610586578063bbe4b803146105a8578063bff232c1146105b2578063c2b40ae4146105c5578063c3a0ec30146105e5578063c8fe182d146105f6578063ca2869a0146105fe578063cd6dc6871461061e578063cf90b6ed14610631578063da881e5a1461063b578063dbb06c931461064e578063e30c397814610661578063e4be6e3d14610669578063e4d185db1461067c578063e59e46951461068f578063e6745e13146106a2578063e82f3b70146106b5578063ebf0c717146106c8578063f1650536146106d0578063f2fde38b146106ea578063f379b0df146106fd578063f52fd80314610737578063f6fc05d5146107a8575b600080fd5b6102ab6102a6366004613cfe565b6107b1565b60405190151581526020015b60405180910390f35b6102d36102ce366004613d3d565b6107e8565b005b6102e86102e3366004613d5a565b610927565b6040519081526020016102b7565b6102d3610304366004613d3d565b610961565b6102d3610317366004613dbb565b610a44565b6102d361032a366004613d5a565b610d05565b61034261033d366004613d5a565b610d7f565b6040516102b7929190613ee8565b6102e8600181565b6102e8610366366004613d5a565b610f2f565b6102ab610379366004613f16565b610f7c565b6102ab61038c366004613d3d565b61115d565b6102d361039f366004613d3d565b61120e565b6102ab6103b2366004613d5a565b611308565b6102ab6103c5366004613d3d565b60066020526000908152604090205460ff1681565b6102e86202a30081565b6102ab6103f2366004613f53565b611349565b600d5461040a906001600160a01b031681565b6040516102b79190613f83565b6102d3610425366004613d3d565b61138e565b6102d3611405565b6102d3611429565b61044d610448366004613f97565b611468565b6040805192835263ffffffff9091166020830152016102b7565b60015461040a906001600160a01b031681565b6102d3610488366004613d3d565b61160a565b6102e861049b366004613d5a565b60096020526000908152604090205481565b600454600160281b900464ffffffffff166102e8565b61040a611755565b600b5461040a906001600160a01b031681565b6102ab6104ec366004613d3d565b611770565b6105186104ff366004613d3d565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102b7565b6102e860035481565b61054a610545366004613d5a565b61178e565b6040516102b79190613fcf565b6102ab610565366004613f53565b611827565b6102d3610578366004613d3d565b61186c565b6102e8600e5481565b610599610594366004613d5a565b611903565b6040516102b793929190613fe2565b6102e86210000081565b6102d36105c0366004613d3d565b611a56565b6102e86105d3366004613d5a565b60086020526000908152604090205481565b6001546001600160a01b031661040a565b6102d3611acf565b6102e861060c366004613d5a565b60009081526008602052604090205490565b6102d361062c366004614025565b611b3a565b6102e862093a8081565b6102ab610649366004613d5a565b611c99565b60005461040a906001600160a01b031681565b61040a611f8e565b600c5461040a906001600160a01b031681565b61040a61068a366004614051565b611f99565b6102d361069d366004613d3d565b61203a565b6102d36106b0366004614051565b6120b3565b6102e86106c3366004613d5a565b61227c565b6102e86122ae565b6106d8601481565b60405160ff90911681526020016102b7565b6102d36106f8366004613d3d565b6122c1565b6004546107199064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102b7565b610779610745366004613d5a565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102b7949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e860025481565b60006001600160e01b03198216635a23ad1360e01b14806107e257506001600160e01b031982166301ffc9a760e01b145b92915050565b6107f0611755565b6001600160a01b0316336001600160a01b0316148061081957506001546001600160a01b031633145b61083657604051632864c4e160e01b815260040160405180910390fd5b61083f81611770565b8190610868576040516381e5828960e01b815260040161085f9190613f83565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff16906108979060049083612332565b6001600160a01b0382166000908152600660205260408120805460ff1916905560028054916108c583614089565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d59261091b92869291600160281b900464ffffffffff16906140a0565b60405180910390a25050565b6000818152600a602052604081206004810154610957576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61096961257e565b600c546001600160a01b0316156109925760405162035f5560e61b815260040160405180910390fd5b6001600160a01b0381166109b95760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610a1f57600d80546001600160a01b031981169091556000600e8190556040516001600160a01b039092169182916000805160206149d483398151915291a2505b6040516001600160a01b038216906000805160206149f483398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610a6a57610a6a6140c1565b14610a8857604051634f4b461f60e11b815260040160405180910390fd5b600481015415610aab5760405163632a22bb60e01b815260040160405180910390fd5b85610ac957604051636caad1ed60e11b815260040160405180910390fd5b6000610b3082600601805480602002602001604051908101604052809291908181526020018280548015610b2657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b08575b50505050506125b2565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610b99573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610bc19190810190614275565b9050806101c0015115610c4f57610c4f8b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610c3f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c21575b50505050508c878d8d8d8d6126be565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610c81908e908c906004016143d9565b600060405180830381600087803b158015610c9b57600080fd5b505af1158015610caf573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610cf096959493929190614450565b60405180910390a25050505050505050505050565b610d0d61257e565b60018110158015610d21575062093a808111155b8190610d435760405163028237cd60e61b815260040161085f91815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610db657610db66140d7565b604051908082528060200260200182016040528015610ddf578160200160208202803683370190505b509450806001600160401b03811115610dfa57610dfa6140d7565b604051908082528060200260200182016040528015610e23578160200160208202803683370190505b5093506000805b83811015610f25576000856006018281548110610e4957610e4961449e565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610e9157610e916140c1565b03610f1c5780888481518110610ea957610ea961449e565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610f0357610f0361449e565b602090810291909101015282610f18816144b4565b9350505b50600101610e2a565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610f5457610f546140c1565b03610f7257604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610fa85760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff166003811115610fcd57610fcd6140c1565b14610feb576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015611035573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105991906144cd565b90508061106c60408601602087016144fa565b63ffffffff16111561108460408601602087016144fa565b8290916110b2576040516344ec930f60e01b815263ffffffff9092166004830152602482015260440161085f565b5050815460ff191660019081178355820185905542600283018190556003546110da91614515565b60038301556110ee60058301856002613c0c565b506110f76122ae565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611149928a928a9291614528565b60405180910390a250600195945050505050565b600061116882611770565b61117457506000919050565b6001546001600160a01b031661119d576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906111cd908590600401613f83565b602060405180830381865afa1580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e29190614579565b61121661257e565b600d546001600160a01b0316806112405760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461128157604051630d77758160e31b81526001600160a01b0392831660048201529116602482015260440161085f565b505060006202a300600e546112969190614515565b90508042818110156112bd576040516337c8270b60e01b815260040161085f9291906143d9565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e8190556040516000805160206149f48339815191529190a2505050565b6000818152600a602052604081206001815460ff16600381111561132e5761132e6140c1565b1461133c5750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611386576113866140c1565b149392505050565b61139661257e565b6001600160a01b0381166113bd5760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61140d61257e565b6040516001623f026d60e01b0319815260040160405180910390fd5b3380611433611f8e565b6001600160a01b03161461145c578060405163118cdaa760e01b815260040161085f9190613f83565b61146581612774565b50565b600b5460009081906001600160a01b031633146114985760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff1660038111156114be576114be6140c1565b146114dc57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561151d5761151d6140c1565b1461152d57600b01549150611602565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b820180549161156283614089565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b84986866040516115aa9291906143d9565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b611612611755565b6001600160a01b0316336001600160a01b0316148061163b57506001546001600160a01b031633145b61165857604051632864c4e160e01b815260040160405180910390fd5b61166181611770565b61146557600454600160281b900464ffffffffff16621000008110611699576040516335b4ac3f60e01b815260040160405180910390fd5b6116ad60046001600160a01b03841661279b565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916116ff836144b4565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539261091b92869291600160281b900464ffffffffff16906140a0565b600080611760612916565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a602052604090206004810154606091906117c2576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561181a57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116117fc575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611863576118636140c1565b14159392505050565b61187461257e565b6001600160a01b03811661189b5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f25906118ef906202a30090614515565b60405190815260200160405180910390a250565b60008181526009602052604090205460609081908190611936576040516322e679e360e11b815260040160405180910390fd5b6000848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561199e57602002820191906000526020600020905b81548152602001906001019080831161198a575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156119f057602002820191906000526020600020905b8154815260200190600101908083116119dc575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611a4257602002820191906000526020600020905b815481526020019060010190808311611a2e575b505050505090509250925092509193909250565b611a5e61257e565b6001600160a01b038116611a855760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611ad761257e565b600d546001600160a01b031680611b015760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b038316916000805160206149d483398151915291a250565b6000611b4461293a565b805490915060ff600160401b82041615906001600160401b0316600081158015611b6b5750825b90506000826001600160401b03166001148015611b875750303b155b905081158015611b95575080155b15611bb35760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611bdc57845460ff60401b1916600160401b1785555b6001600160a01b038716611c035760405163d92e233d60e01b815260040160405180910390fd5b611c0c33612963565b611c1860046014612974565b611c2186610d05565b611c29611755565b6001600160a01b0316876001600160a01b031614611c4a57611c4a87612774565b8315611c9057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611cbe57611cbe6140c1565b03611cdc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611cf457611cf46140c1565b14611d1257604051631860f69960e31b815260040160405180910390fd5b80600301544211611d3657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611e22578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b158015611e0057600080fd5b505af1158015611e14573d6000803e3d6000fd5b506000979650505050505050565b611e2b826129b3565b815460ff191660021782556006820154600b83018190556000816001600160401b03811115611e5c57611e5c6140d7565b604051908082528060200260200182016040528015611e85578160200160208202803683370190505b50905060005b82811015611efa57846009016000866006018381548110611eae57611eae61449e565b60009182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611ee757611ee761449e565b6020908102919091010152600101611e8b565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611f4157600080fd5b505af1158015611f55573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e58560060183604051611149929190614594565b600080611760612ae4565b6000828152600a602052604081206002815460ff166003811115611fbf57611fbf6140c1565b14611fdd57604051634f4b461f60e11b815260040160405180910390fd5b60068101548390808210612006576040516326c5c55b60e11b815260040161085f9291906143d9565b505080600601838154811061201d5761201d61449e565b6000918252602090912001546001600160a01b0316949350505050565b61204261257e565b6001600160a01b0381166120695760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156120d8576120d86140c1565b036120f657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561210e5761210e6140c1565b1461212c57604051631860f69960e31b815260040160405180910390fd5b806003015442111561215157604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff16156121845760405163257309f160e11b815260040160405180910390fd5b61218d3361115d565b6121aa5760405163149fbcfd60e11b815260040160405180910390fd5b6121b5338385612b08565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff1916600117905590915061223190839083612ce4565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd858460405161226e9291906143d9565b60405180910390a350505050565b600081815260096020526040902054806122a9576040516322e679e360e11b815260040160405180910390fd5b919050565b60006122bc60046014612ef0565b905090565b6122c961257e565b60006122d3612ae4565b80546001600160a01b0319166001600160a01b03841690811782559091506122f9611755565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614a14833981519152821061234c57600080fd5b825464ffffffffff600160281b9091048116908216811161236c57600080fd5b8260005b818660010160006123818488612f56565b64ffffffffff1681526020019081526020016000208190555060008160016123a991906145a7565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116123de5750612576565b600185166000036124aa5760006123ff836123fa8860016145c0565b612f56565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612461916004016145dd565b602060405180830381865af415801561247e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a291906144cd565b935050612562565b60006124bb836123fa60018961460e565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161251d916004016145dd565b602060405180830381865af415801561253a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061255e91906144cd565b9350505b50647fffffffff600194851c169301612370565b505050505050565b33612587611755565b6001600160a01b0316146125b0573360405163118cdaa760e01b815260040161085f9190613f83565b565b8051600090816125c382601461462b565b6001600160401b038111156125da576125da6140d7565b6040519080825280601f01601f191660200182016040528015612604576020820181803683370190505b50905060005b828110156126ae5760008582815181106126265761262661449e565b602002602001015160601b90506000826014612642919061462b565b905060005b60148110156126a0578281601481106126625761266261449e565b1a60f81b856126718385614515565b815181106126815761268161449e565b60200101906001600160f81b031916908160001a905350600101612647565b50505080600101905061260a565b5080516020909101209392505050565b826126dc57604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b81526004016127199796959493929190614642565b602060405180830381865afa158015612736573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275a9190614579565b506127688a85858585612f74565b50505050505050505050565b600061277e612ae4565b80546001600160a01b03191681559050612797826130b5565b5050565b8154600160281b900464ffffffffff16600080516020614a1483398151915282106127c557600080fd5b825464ffffffffff908116908216106127dd57600080fd5b6127e88160016145c0565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b8185600101600061281f8487612f56565b64ffffffffff168152602081019190915260400160002055600183161561290f576000612851826123fa60018761460e565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916128b3916004016145dd565b602060405180830381865af41580156128d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f491906144cd565b647fffffffff600195861c169490935091909101905061280e565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006107e2565b61296b613111565b61146581613136565b602060ff8216111561298557600080fd5b612996600160ff831681901b61468e565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612adf5760006129d0826001614515565b90505b82811015612ad65760008460060183815481106129f2576129f261449e565b60009182526020822001546006870180546001600160a01b0390921693509084908110612a2157612a2161449e565b6000918252602090912001546001600160a01b0390811691508216811015612acc5780866006018581548110612a5957612a5961449e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612a9d57612a9d61449e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b50506001016129d3565b506001016129bb565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612b295760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612b52576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612b899161468e565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612bd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf691906144cd565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c7191906144cd565b905060008111612c945760405163aeaddff160e01b815260040160405180910390fd5b6000612ca082846146a1565b905060008111612cc35760405163149fbcfd60e11b815260040160405180910390fd5b80861115611c905760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612d6457508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612ee9565b60008087600901600085600081548110612d8057612d8061449e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612e0c576000896009016000878481548110612dcd57612dcd61449e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e03578092508193505b50600101612daa565b50808610612e21576000945050505050612ee9565b600088600a016000868581548110612e3b57612e3b61449e565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612e7957612e796140c1565b021790555086848381548110612e9157612e9161449e565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff1611612f0157600080fd5b602060ff83161115612f1257600080fd5b8254600160281b900464ffffffffff1680612f3160ff851660026147d3565b64ffffffffff161015612f4357600080fd5b612f4e848285613168565b949350505050565b600081612f6a60ff851663ffffffff6147ed565b612ee991906145c0565b80612f9257604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b0316612fbb57604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e90612ffc90309046908d908d908d908d908d90600401614814565b600060405180830381865afa158015613019573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261304191908101906148e5565b60008b8152600f60209081526040909120845194975092955090935061306a9290860190613cae565b506000888152601060209081526040909120835161308a92850190613cae565b50600088815260116020908152604090912082516130aa92840190613cae565b505050505050505050565b60006130bf612916565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b613119613220565b6125b057604051631afcd79f60e31b815260040160405180910390fd5b61313e613111565b6001600160a01b03811661145c576000604051631e4fbdf760e01b815260040161085f9190613f83565b6000602060ff8316111561317b57600080fd5b8264ffffffffff1660000361319a576131938261323a565b9050612ee9565b60006131a78360016145a7565b60ff166001600160401b038111156131c1576131c16140d7565b6040519080825280602002602001820160405280156131ea578160200160208202803683370190505b5090506131f98585858461388f565b808360ff168151811061320e5761320e61449e565b60200260200101519150509392505050565b600061322a61293a565b54600160401b900460ff16919050565b60008160ff1660000361324f57506000919050565b8160ff1660010361328157507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036132b357507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036132e557507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361331757507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361334957507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361337b57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036133ad57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036133df57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361341157507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361344357507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361347557507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c036134a757507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036134d957507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361350b57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361353d57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361356f57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff166011036135a157507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff166012036135d357507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361360557507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361363757507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361366957507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361369b57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff166017036136cd57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036136ff57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361373157507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361376357507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361379557507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c036137c757507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036137f957507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361382b57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361385d57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361029357507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff831611156138a057600080fd5b60008364ffffffffff16116138b457600080fd5b60006138c160018561460e565b905060018116600003613919578460010160006138df600084612f56565b64ffffffffff16815260200190815260200160002054826000815181106139085761390861449e565b602002602001018181525050613943565b613923600061323a565b826000815181106139365761393661449e565b6020026020010181815250505b60005b8360ff168160ff1610156125765760018216600003613a3f5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff16815181106139995761399961449e565b602002602001015181526020016139af8561323a565b8152506040518263ffffffff1660e01b81526004016139ce91906145dd565b602060405180830381865af41580156139eb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a0f91906144cd565b83613a1b8360016145a7565b60ff1681518110613a2e57613a2e61449e565b602002602001018181525050613bf9565b6000613a4c8260016145a7565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613af1576000876001016000613aa5856001613a9491906145a7565b60018864ffffffffff16901c612f56565b64ffffffffff1681526020019081526020016000205490508085846001613acc91906145a7565b60ff1681518110613adf57613adf61449e565b60200260200101818152505050613bf7565b6000876001016000613b0a856001886123fa919061460e565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613b6257613b6261449e565b60200260200101518152506040518263ffffffff1660e01b8152600401613b8991906145dd565b602060405180830381865af4158015613ba6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bca91906144cd565b85613bd68560016145a7565b60ff1681518110613be957613be961449e565b602002602001018181525050505b505b647fffffffff600192831c169101613946565b600183019183908215613c9e5791602002820160005b83821115613c6c57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613c22565b8015613c9c5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613c6c565b505b50613caa929150613ce9565b5090565b828054828255906000526020600020908101928215613c9e579160200282015b82811115613c9e578251825591602001919060010190613cce565b5b80821115613caa5760008155600101613cea565b600060208284031215613d1057600080fd5b81356001600160e01b031981168114612ee957600080fd5b6001600160a01b038116811461146557600080fd5b600060208284031215613d4f57600080fd5b8135612ee981613d28565b600060208284031215613d6c57600080fd5b5035919050565b60008083601f840112613d8557600080fd5b5081356001600160401b03811115613d9c57600080fd5b602083019150836020828501011115613db457600080fd5b9250929050565b60008060008060008060008060a0898b031215613dd757600080fd5b8835975060208901356001600160401b03811115613df457600080fd5b613e008b828c01613d73565b9098509650506040890135945060608901356001600160401b03811115613e2657600080fd5b613e328b828c01613d73565b90955093505060808901356001600160401b03811115613e5157600080fd5b613e5d8b828c01613d73565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b82811015613eac5781516001600160a01b0316865260209586019590910190600101613e85565b5093949350505050565b600081518084526020840193506020830160005b82811015613eac578151865260209586019590910190600101613eca565b604081526000613efb6040830185613e71565b8281036020840152613f0d8185613eb6565b95945050505050565b600080600060808486031215613f2b57600080fd5b833592506020840135915060808401851015613f4657600080fd5b6040840190509250925092565b60008060408385031215613f6657600080fd5b823591506020830135613f7881613d28565b809150509250929050565b6001600160a01b0391909116815260200190565b600080600060608486031215613fac57600080fd5b833592506020840135613fbe81613d28565b929592945050506040919091013590565b602081526000612ee96020830184613e71565b606081526000613ff56060830186613eb6565b82810360208401526140078186613eb6565b9050828103604084015261401b8185613eb6565b9695505050505050565b6000806040838503121561403857600080fd5b823561404381613d28565b946020939093013593505050565b6000806040838503121561406457600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161409857614098614073565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b0381118282101715614110576141106140d7565b60405290565b604051601f8201601f191681016001600160401b038111828210171561413e5761413e6140d7565b604052919050565b8051600481106122a957600080fd5b600082601f83011261416657600080fd5b604080519081016001600160401b0381118282101715614188576141886140d7565b806040525080604084018581111561419f57600080fd5b845b818110156141b95780518352602092830192016141a1565b509195945050505050565b80516122a981613d28565b805160ff811681146122a957600080fd5b600082601f8301126141f157600080fd5b81516001600160401b0381111561420a5761420a6140d7565b61421d601f8201601f1916602001614116565b81815284602083860101111561423257600080fd5b60005b8281101561425157602081860181015183830182015201614235565b506000918101602001919091529392505050565b805180151581146122a957600080fd5b60006020828403121561428757600080fd5b81516001600160401b0381111561429d57600080fd5b820161020081850312156142b057600080fd5b6142b86140ed565b815181526142c860208301614146565b6020820152604082810151908201526142e48560608401614155565b606082015260a082015160808201526142ff60c083016141c4565b60a082015261431060e083016141cf565b60c08201526101008201516001600160401b0381111561432f57600080fd5b61433b868285016141e0565b60e08301525061434e61012083016141c4565b61010082015261436161014083016141c4565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561439857600080fd5b6143a4868285016141e0565b610180830152506143b86101c083016141c4565b6101a08201526143cb6101e08301614265565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b82811015613eac5781546001600160a01b0316865260209095019460019182019101614400565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60808152600061446360808301896143e7565b828103602084015261447681888a614427565b90508560408401528281036060840152614491818587614427565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016144c6576144c6614073565b5060010190565b6000602082840312156144df57600080fd5b5051919050565b803563ffffffff811681146122a957600080fd5b60006020828403121561450c57600080fd5b612ee9826144e6565b808201808211156107e2576107e2614073565b84815260a08101602082018560005b60028110156145645763ffffffff61454e836144e6565b1683526020928301929190910190600101614537565b50505060608201939093526080015292915050565b60006020828403121561458b57600080fd5b612ee982614265565b604081526000613efb60408301856143e7565b60ff81811683821601908111156107e2576107e2614073565b64ffffffffff81811683821601908111156107e2576107e2614073565b60408101818360005b60028110156146055781518352602092830192909101906001016145e6565b50505092915050565b64ffffffffff82811682821603908111156107e2576107e2614073565b80820281158282048414176107e2576107e2614073565b87815286602082015260c06040820152600061466160c0830188613e71565b86606084015285608084015282810360a0840152614680818587614427565b9a9950505050505050505050565b818103818111156107e2576107e2614073565b6000826146be57634e487b7160e01b600052601260045260246000fd5b500490565b6001815b6001841115611602578085048111156146e2576146e2614073565b60018416156146f057908102905b60019390931c9280026146c7565b60008261470d575060016107e2565b8161471a575060006107e2565b8160018114614730576002811461473a5761476c565b60019150506107e2565b60ff84111561474b5761474b614073565b6001841b915064ffffffffff82111561476657614766614073565b506107e2565b5060208310610133831016604e8410600b84101617156147a4575081810a64ffffffffff81111561479f5761479f614073565b6107e2565b6147b464ffffffffff84846146c3565b8064ffffffffff048211156147cb576147cb614073565b029392505050565b6000612ee964ffffffffff841664ffffffffff84166146fe565b64ffffffffff818116838216029081169081811461480d5761480d614073565b5092915050565b60018060a01b038816815286602082015285604082015260a06060820152600061484260a083018688614427565b8281036080840152614680818587614427565b60006001600160401b0382111561486e5761486e6140d7565b5060051b60200190565b600082601f83011261488957600080fd5b815161489c61489782614855565b614116565b8082825260208201915060208360051b8601019250858311156148be57600080fd5b602085015b838110156148db5780518352602092830192016148c3565b5095945050505050565b6000806000606084860312156148fa57600080fd5b83516001600160401b0381111561491057600080fd5b8401601f8101861361492157600080fd5b805161492f61489782614855565b8082825260208201915060208360051b85010192508883111561495157600080fd5b6020840193505b82841015614973578351825260209384019390910190614958565b6020880151909650925050506001600160401b0381111561499357600080fd5b61499f86828701614878565b604086015190935090506001600160401b038111156149bd57600080fd5b6149c986828701614878565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102935760003560e01c806301ffc9a714610298578063096b810a146102c0578063099a161a146102d55780630b45f8e9146102f65780630bbfade7146103095780630f3e34121461031c57806317d611201461032f5780631e08d0e8146103505780632800d82914610358578063291a691b1461036b5780632e7b716d1461037e578063323beaa5146103915780634d6861a6146103a457806350e6d94c146103b757806350e6fad3146103da5780635d504776146103e457806362cc89a8146103f757806370e36bbe14610417578063715018a61461042a57806379ba5097146104325780637c92f5241461043a57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ad5780638da5cb5b146104c35780638e5ce3ad146104cb5780639015d371146104de5780639a7a2ffc146104f15780639f0f874a1461052e578063a016493014610537578063a8a4d69b14610557578063acc524941461056a578063b2d5d1ac1461057d578063b8ab470414610586578063bbe4b803146105a8578063bff232c1146105b2578063c2b40ae4146105c5578063c3a0ec30146105e5578063c8fe182d146105f6578063ca2869a0146105fe578063cd6dc6871461061e578063cf90b6ed14610631578063da881e5a1461063b578063dbb06c931461064e578063e30c397814610661578063e4be6e3d14610669578063e4d185db1461067c578063e59e46951461068f578063e6745e13146106a2578063e82f3b70146106b5578063ebf0c717146106c8578063f1650536146106d0578063f2fde38b146106ea578063f379b0df146106fd578063f52fd80314610737578063f6fc05d5146107a8575b600080fd5b6102ab6102a6366004613cfe565b6107b1565b60405190151581526020015b60405180910390f35b6102d36102ce366004613d3d565b6107e8565b005b6102e86102e3366004613d5a565b610927565b6040519081526020016102b7565b6102d3610304366004613d3d565b610961565b6102d3610317366004613dbb565b610a44565b6102d361032a366004613d5a565b610d05565b61034261033d366004613d5a565b610d7f565b6040516102b7929190613ee8565b6102e8600181565b6102e8610366366004613d5a565b610f2f565b6102ab610379366004613f16565b610f7c565b6102ab61038c366004613d3d565b61115d565b6102d361039f366004613d3d565b61120e565b6102ab6103b2366004613d5a565b611308565b6102ab6103c5366004613d3d565b60066020526000908152604090205460ff1681565b6102e86202a30081565b6102ab6103f2366004613f53565b611349565b600d5461040a906001600160a01b031681565b6040516102b79190613f83565b6102d3610425366004613d3d565b61138e565b6102d3611405565b6102d3611429565b61044d610448366004613f97565b611468565b6040805192835263ffffffff9091166020830152016102b7565b60015461040a906001600160a01b031681565b6102d3610488366004613d3d565b61160a565b6102e861049b366004613d5a565b60096020526000908152604090205481565b600454600160281b900464ffffffffff166102e8565b61040a611755565b600b5461040a906001600160a01b031681565b6102ab6104ec366004613d3d565b611770565b6105186104ff366004613d3d565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102b7565b6102e860035481565b61054a610545366004613d5a565b61178e565b6040516102b79190613fcf565b6102ab610565366004613f53565b611827565b6102d3610578366004613d3d565b61186c565b6102e8600e5481565b610599610594366004613d5a565b611903565b6040516102b793929190613fe2565b6102e86210000081565b6102d36105c0366004613d3d565b611a56565b6102e86105d3366004613d5a565b60086020526000908152604090205481565b6001546001600160a01b031661040a565b6102d3611acf565b6102e861060c366004613d5a565b60009081526008602052604090205490565b6102d361062c366004614025565b611b3a565b6102e862093a8081565b6102ab610649366004613d5a565b611c99565b60005461040a906001600160a01b031681565b61040a611f8e565b600c5461040a906001600160a01b031681565b61040a61068a366004614051565b611f99565b6102d361069d366004613d3d565b61203a565b6102d36106b0366004614051565b6120b3565b6102e86106c3366004613d5a565b61227c565b6102e86122ae565b6106d8601481565b60405160ff90911681526020016102b7565b6102d36106f8366004613d3d565b6122c1565b6004546107199064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102b7565b610779610745366004613d5a565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102b7949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e860025481565b60006001600160e01b03198216635a23ad1360e01b14806107e257506001600160e01b031982166301ffc9a760e01b145b92915050565b6107f0611755565b6001600160a01b0316336001600160a01b0316148061081957506001546001600160a01b031633145b61083657604051632864c4e160e01b815260040160405180910390fd5b61083f81611770565b8190610868576040516381e5828960e01b815260040161085f9190613f83565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff16906108979060049083612332565b6001600160a01b0382166000908152600660205260408120805460ff1916905560028054916108c583614089565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d59261091b92869291600160281b900464ffffffffff16906140a0565b60405180910390a25050565b6000818152600a602052604081206004810154610957576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61096961257e565b600c546001600160a01b0316156109925760405162035f5560e61b815260040160405180910390fd5b6001600160a01b0381166109b95760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610a1f57600d80546001600160a01b031981169091556000600e8190556040516001600160a01b039092169182916000805160206149d483398151915291a2505b6040516001600160a01b038216906000805160206149f483398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610a6a57610a6a6140c1565b14610a8857604051634f4b461f60e11b815260040160405180910390fd5b600481015415610aab5760405163632a22bb60e01b815260040160405180910390fd5b85610ac957604051636caad1ed60e11b815260040160405180910390fd5b6000610b3082600601805480602002602001604051908101604052809291908181526020018280548015610b2657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b08575b50505050506125b2565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610b99573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610bc19190810190614275565b9050806101c0015115610c4f57610c4f8b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610c3f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c21575b50505050508c878d8d8d8d6126be565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610c81908e908c906004016143d9565b600060405180830381600087803b158015610c9b57600080fd5b505af1158015610caf573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610cf096959493929190614450565b60405180910390a25050505050505050505050565b610d0d61257e565b60018110158015610d21575062093a808111155b8190610d435760405163028237cd60e61b815260040161085f91815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610db657610db66140d7565b604051908082528060200260200182016040528015610ddf578160200160208202803683370190505b509450806001600160401b03811115610dfa57610dfa6140d7565b604051908082528060200260200182016040528015610e23578160200160208202803683370190505b5093506000805b83811015610f25576000856006018281548110610e4957610e4961449e565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610e9157610e916140c1565b03610f1c5780888481518110610ea957610ea961449e565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610f0357610f0361449e565b602090810291909101015282610f18816144b4565b9350505b50600101610e2a565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610f5457610f546140c1565b03610f7257604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610fa85760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff166003811115610fcd57610fcd6140c1565b14610feb576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015611035573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105991906144cd565b90508061106c60408601602087016144fa565b63ffffffff16111561108460408601602087016144fa565b8290916110b2576040516344ec930f60e01b815263ffffffff9092166004830152602482015260440161085f565b5050815460ff191660019081178355820185905542600283018190556003546110da91614515565b60038301556110ee60058301856002613c0c565b506110f76122ae565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611149928a928a9291614528565b60405180910390a250600195945050505050565b600061116882611770565b61117457506000919050565b6001546001600160a01b031661119d576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906111cd908590600401613f83565b602060405180830381865afa1580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e29190614579565b61121661257e565b600d546001600160a01b0316806112405760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461128157604051630d77758160e31b81526001600160a01b0392831660048201529116602482015260440161085f565b505060006202a300600e546112969190614515565b90508042818110156112bd576040516337c8270b60e01b815260040161085f9291906143d9565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e8190556040516000805160206149f48339815191529190a2505050565b6000818152600a602052604081206001815460ff16600381111561132e5761132e6140c1565b1461133c5750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611386576113866140c1565b149392505050565b61139661257e565b6001600160a01b0381166113bd5760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61140d61257e565b6040516001623f026d60e01b0319815260040160405180910390fd5b3380611433611f8e565b6001600160a01b03161461145c578060405163118cdaa760e01b815260040161085f9190613f83565b61146581612774565b50565b600b5460009081906001600160a01b031633146114985760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff1660038111156114be576114be6140c1565b146114dc57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561151d5761151d6140c1565b1461152d57600b01549150611602565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b820180549161156283614089565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b84986866040516115aa9291906143d9565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b611612611755565b6001600160a01b0316336001600160a01b0316148061163b57506001546001600160a01b031633145b61165857604051632864c4e160e01b815260040160405180910390fd5b61166181611770565b61146557600454600160281b900464ffffffffff16621000008110611699576040516335b4ac3f60e01b815260040160405180910390fd5b6116ad60046001600160a01b03841661279b565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916116ff836144b4565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539261091b92869291600160281b900464ffffffffff16906140a0565b600080611760612916565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a602052604090206004810154606091906117c2576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561181a57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116117fc575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611863576118636140c1565b14159392505050565b61187461257e565b6001600160a01b03811661189b5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f25906118ef906202a30090614515565b60405190815260200160405180910390a250565b60008181526009602052604090205460609081908190611936576040516322e679e360e11b815260040160405180910390fd5b6000848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561199e57602002820191906000526020600020905b81548152602001906001019080831161198a575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156119f057602002820191906000526020600020905b8154815260200190600101908083116119dc575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611a4257602002820191906000526020600020905b815481526020019060010190808311611a2e575b505050505090509250925092509193909250565b611a5e61257e565b6001600160a01b038116611a855760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611ad761257e565b600d546001600160a01b031680611b015760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b038316916000805160206149d483398151915291a250565b6000611b4461293a565b805490915060ff600160401b82041615906001600160401b0316600081158015611b6b5750825b90506000826001600160401b03166001148015611b875750303b155b905081158015611b95575080155b15611bb35760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611bdc57845460ff60401b1916600160401b1785555b6001600160a01b038716611c035760405163d92e233d60e01b815260040160405180910390fd5b611c0c33612963565b611c1860046014612974565b611c2186610d05565b611c29611755565b6001600160a01b0316876001600160a01b031614611c4a57611c4a87612774565b8315611c9057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611cbe57611cbe6140c1565b03611cdc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611cf457611cf46140c1565b14611d1257604051631860f69960e31b815260040160405180910390fd5b80600301544211611d3657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611e22578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b158015611e0057600080fd5b505af1158015611e14573d6000803e3d6000fd5b506000979650505050505050565b611e2b826129b3565b815460ff191660021782556006820154600b83018190556000816001600160401b03811115611e5c57611e5c6140d7565b604051908082528060200260200182016040528015611e85578160200160208202803683370190505b50905060005b82811015611efa57846009016000866006018381548110611eae57611eae61449e565b60009182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611ee757611ee761449e565b6020908102919091010152600101611e8b565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611f4157600080fd5b505af1158015611f55573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e58560060183604051611149929190614594565b600080611760612ae4565b6000828152600a602052604081206002815460ff166003811115611fbf57611fbf6140c1565b14611fdd57604051634f4b461f60e11b815260040160405180910390fd5b60068101548390808210612006576040516326c5c55b60e11b815260040161085f9291906143d9565b505080600601838154811061201d5761201d61449e565b6000918252602090912001546001600160a01b0316949350505050565b61204261257e565b6001600160a01b0381166120695760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156120d8576120d86140c1565b036120f657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561210e5761210e6140c1565b1461212c57604051631860f69960e31b815260040160405180910390fd5b806003015442111561215157604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff16156121845760405163257309f160e11b815260040160405180910390fd5b61218d3361115d565b6121aa5760405163149fbcfd60e11b815260040160405180910390fd5b6121b5338385612b08565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff1916600117905590915061223190839083612ce4565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd858460405161226e9291906143d9565b60405180910390a350505050565b600081815260096020526040902054806122a9576040516322e679e360e11b815260040160405180910390fd5b919050565b60006122bc60046014612ef0565b905090565b6122c961257e565b60006122d3612ae4565b80546001600160a01b0319166001600160a01b03841690811782559091506122f9611755565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614a14833981519152821061234c57600080fd5b825464ffffffffff600160281b9091048116908216811161236c57600080fd5b8260005b818660010160006123818488612f56565b64ffffffffff1681526020019081526020016000208190555060008160016123a991906145a7565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116123de5750612576565b600185166000036124aa5760006123ff836123fa8860016145c0565b612f56565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612461916004016145dd565b602060405180830381865af415801561247e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a291906144cd565b935050612562565b60006124bb836123fa60018961460e565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161251d916004016145dd565b602060405180830381865af415801561253a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061255e91906144cd565b9350505b50647fffffffff600194851c169301612370565b505050505050565b33612587611755565b6001600160a01b0316146125b0573360405163118cdaa760e01b815260040161085f9190613f83565b565b8051600090816125c382601461462b565b6001600160401b038111156125da576125da6140d7565b6040519080825280601f01601f191660200182016040528015612604576020820181803683370190505b50905060005b828110156126ae5760008582815181106126265761262661449e565b602002602001015160601b90506000826014612642919061462b565b905060005b60148110156126a0578281601481106126625761266261449e565b1a60f81b856126718385614515565b815181106126815761268161449e565b60200101906001600160f81b031916908160001a905350600101612647565b50505080600101905061260a565b5080516020909101209392505050565b826126dc57604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b81526004016127199796959493929190614642565b602060405180830381865afa158015612736573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275a9190614579565b506127688a85858585612f74565b50505050505050505050565b600061277e612ae4565b80546001600160a01b03191681559050612797826130b5565b5050565b8154600160281b900464ffffffffff16600080516020614a1483398151915282106127c557600080fd5b825464ffffffffff908116908216106127dd57600080fd5b6127e88160016145c0565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b8185600101600061281f8487612f56565b64ffffffffff168152602081019190915260400160002055600183161561290f576000612851826123fa60018761460e565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916128b3916004016145dd565b602060405180830381865af41580156128d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f491906144cd565b647fffffffff600195861c169490935091909101905061280e565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006107e2565b61296b613111565b61146581613136565b602060ff8216111561298557600080fd5b612996600160ff831681901b61468e565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612adf5760006129d0826001614515565b90505b82811015612ad65760008460060183815481106129f2576129f261449e565b60009182526020822001546006870180546001600160a01b0390921693509084908110612a2157612a2161449e565b6000918252602090912001546001600160a01b0390811691508216811015612acc5780866006018581548110612a5957612a5961449e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612a9d57612a9d61449e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b50506001016129d3565b506001016129bb565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612b295760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612b52576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612b899161468e565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612bd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf691906144cd565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c7191906144cd565b905060008111612c945760405163aeaddff160e01b815260040160405180910390fd5b6000612ca082846146a1565b905060008111612cc35760405163149fbcfd60e11b815260040160405180910390fd5b80861115611c905760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612d6457508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612ee9565b60008087600901600085600081548110612d8057612d8061449e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612e0c576000896009016000878481548110612dcd57612dcd61449e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e03578092508193505b50600101612daa565b50808610612e21576000945050505050612ee9565b600088600a016000868581548110612e3b57612e3b61449e565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612e7957612e796140c1565b021790555086848381548110612e9157612e9161449e565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff1611612f0157600080fd5b602060ff83161115612f1257600080fd5b8254600160281b900464ffffffffff1680612f3160ff851660026147d3565b64ffffffffff161015612f4357600080fd5b612f4e848285613168565b949350505050565b600081612f6a60ff851663ffffffff6147ed565b612ee991906145c0565b80612f9257604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b0316612fbb57604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e90612ffc90309046908d908d908d908d908d90600401614814565b600060405180830381865afa158015613019573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261304191908101906148e5565b60008b8152600f60209081526040909120845194975092955090935061306a9290860190613cae565b506000888152601060209081526040909120835161308a92850190613cae565b50600088815260116020908152604090912082516130aa92840190613cae565b505050505050505050565b60006130bf612916565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b613119613220565b6125b057604051631afcd79f60e31b815260040160405180910390fd5b61313e613111565b6001600160a01b03811661145c576000604051631e4fbdf760e01b815260040161085f9190613f83565b6000602060ff8316111561317b57600080fd5b8264ffffffffff1660000361319a576131938261323a565b9050612ee9565b60006131a78360016145a7565b60ff166001600160401b038111156131c1576131c16140d7565b6040519080825280602002602001820160405280156131ea578160200160208202803683370190505b5090506131f98585858461388f565b808360ff168151811061320e5761320e61449e565b60200260200101519150509392505050565b600061322a61293a565b54600160401b900460ff16919050565b60008160ff1660000361324f57506000919050565b8160ff1660010361328157507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036132b357507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036132e557507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361331757507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361334957507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361337b57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036133ad57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036133df57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361341157507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361344357507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361347557507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c036134a757507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036134d957507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361350b57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361353d57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361356f57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff166011036135a157507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff166012036135d357507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361360557507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361363757507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361366957507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361369b57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff166017036136cd57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036136ff57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361373157507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361376357507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361379557507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c036137c757507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036137f957507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361382b57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361385d57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361029357507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff831611156138a057600080fd5b60008364ffffffffff16116138b457600080fd5b60006138c160018561460e565b905060018116600003613919578460010160006138df600084612f56565b64ffffffffff16815260200190815260200160002054826000815181106139085761390861449e565b602002602001018181525050613943565b613923600061323a565b826000815181106139365761393661449e565b6020026020010181815250505b60005b8360ff168160ff1610156125765760018216600003613a3f5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff16815181106139995761399961449e565b602002602001015181526020016139af8561323a565b8152506040518263ffffffff1660e01b81526004016139ce91906145dd565b602060405180830381865af41580156139eb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a0f91906144cd565b83613a1b8360016145a7565b60ff1681518110613a2e57613a2e61449e565b602002602001018181525050613bf9565b6000613a4c8260016145a7565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613af1576000876001016000613aa5856001613a9491906145a7565b60018864ffffffffff16901c612f56565b64ffffffffff1681526020019081526020016000205490508085846001613acc91906145a7565b60ff1681518110613adf57613adf61449e565b60200260200101818152505050613bf7565b6000876001016000613b0a856001886123fa919061460e565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613b6257613b6261449e565b60200260200101518152506040518263ffffffff1660e01b8152600401613b8991906145dd565b602060405180830381865af4158015613ba6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bca91906144cd565b85613bd68560016145a7565b60ff1681518110613be957613be961449e565b602002602001018181525050505b505b647fffffffff600192831c169101613946565b600183019183908215613c9e5791602002820160005b83821115613c6c57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613c22565b8015613c9c5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613c6c565b505b50613caa929150613ce9565b5090565b828054828255906000526020600020908101928215613c9e579160200282015b82811115613c9e578251825591602001919060010190613cce565b5b80821115613caa5760008155600101613cea565b600060208284031215613d1057600080fd5b81356001600160e01b031981168114612ee957600080fd5b6001600160a01b038116811461146557600080fd5b600060208284031215613d4f57600080fd5b8135612ee981613d28565b600060208284031215613d6c57600080fd5b5035919050565b60008083601f840112613d8557600080fd5b5081356001600160401b03811115613d9c57600080fd5b602083019150836020828501011115613db457600080fd5b9250929050565b60008060008060008060008060a0898b031215613dd757600080fd5b8835975060208901356001600160401b03811115613df457600080fd5b613e008b828c01613d73565b9098509650506040890135945060608901356001600160401b03811115613e2657600080fd5b613e328b828c01613d73565b90955093505060808901356001600160401b03811115613e5157600080fd5b613e5d8b828c01613d73565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b82811015613eac5781516001600160a01b0316865260209586019590910190600101613e85565b5093949350505050565b600081518084526020840193506020830160005b82811015613eac578151865260209586019590910190600101613eca565b604081526000613efb6040830185613e71565b8281036020840152613f0d8185613eb6565b95945050505050565b600080600060808486031215613f2b57600080fd5b833592506020840135915060808401851015613f4657600080fd5b6040840190509250925092565b60008060408385031215613f6657600080fd5b823591506020830135613f7881613d28565b809150509250929050565b6001600160a01b0391909116815260200190565b600080600060608486031215613fac57600080fd5b833592506020840135613fbe81613d28565b929592945050506040919091013590565b602081526000612ee96020830184613e71565b606081526000613ff56060830186613eb6565b82810360208401526140078186613eb6565b9050828103604084015261401b8185613eb6565b9695505050505050565b6000806040838503121561403857600080fd5b823561404381613d28565b946020939093013593505050565b6000806040838503121561406457600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161409857614098614073565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b0381118282101715614110576141106140d7565b60405290565b604051601f8201601f191681016001600160401b038111828210171561413e5761413e6140d7565b604052919050565b8051600481106122a957600080fd5b600082601f83011261416657600080fd5b604080519081016001600160401b0381118282101715614188576141886140d7565b806040525080604084018581111561419f57600080fd5b845b818110156141b95780518352602092830192016141a1565b509195945050505050565b80516122a981613d28565b805160ff811681146122a957600080fd5b600082601f8301126141f157600080fd5b81516001600160401b0381111561420a5761420a6140d7565b61421d601f8201601f1916602001614116565b81815284602083860101111561423257600080fd5b60005b8281101561425157602081860181015183830182015201614235565b506000918101602001919091529392505050565b805180151581146122a957600080fd5b60006020828403121561428757600080fd5b81516001600160401b0381111561429d57600080fd5b820161020081850312156142b057600080fd5b6142b86140ed565b815181526142c860208301614146565b6020820152604082810151908201526142e48560608401614155565b606082015260a082015160808201526142ff60c083016141c4565b60a082015261431060e083016141cf565b60c08201526101008201516001600160401b0381111561432f57600080fd5b61433b868285016141e0565b60e08301525061434e61012083016141c4565b61010082015261436161014083016141c4565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561439857600080fd5b6143a4868285016141e0565b610180830152506143b86101c083016141c4565b6101a08201526143cb6101e08301614265565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b82811015613eac5781546001600160a01b0316865260209095019460019182019101614400565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60808152600061446360808301896143e7565b828103602084015261447681888a614427565b90508560408401528281036060840152614491818587614427565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016144c6576144c6614073565b5060010190565b6000602082840312156144df57600080fd5b5051919050565b803563ffffffff811681146122a957600080fd5b60006020828403121561450c57600080fd5b612ee9826144e6565b808201808211156107e2576107e2614073565b84815260a08101602082018560005b60028110156145645763ffffffff61454e836144e6565b1683526020928301929190910190600101614537565b50505060608201939093526080015292915050565b60006020828403121561458b57600080fd5b612ee982614265565b604081526000613efb60408301856143e7565b60ff81811683821601908111156107e2576107e2614073565b64ffffffffff81811683821601908111156107e2576107e2614073565b60408101818360005b60028110156146055781518352602092830192909101906001016145e6565b50505092915050565b64ffffffffff82811682821603908111156107e2576107e2614073565b80820281158282048414176107e2576107e2614073565b87815286602082015260c06040820152600061466160c0830188613e71565b86606084015285608084015282810360a0840152614680818587614427565b9a9950505050505050505050565b818103818111156107e2576107e2614073565b6000826146be57634e487b7160e01b600052601260045260246000fd5b500490565b6001815b6001841115611602578085048111156146e2576146e2614073565b60018416156146f057908102905b60019390931c9280026146c7565b60008261470d575060016107e2565b8161471a575060006107e2565b8160018114614730576002811461473a5761476c565b60019150506107e2565b60ff84111561474b5761474b614073565b6001841b915064ffffffffff82111561476657614766614073565b506107e2565b5060208310610133831016604e8410600b84101617156147a4575081810a64ffffffffff81111561479f5761479f614073565b6107e2565b6147b464ffffffffff84846146c3565b8064ffffffffff048211156147cb576147cb614073565b029392505050565b6000612ee964ffffffffff841664ffffffffff84166146fe565b64ffffffffff818116838216029081169081811461480d5761480d614073565b5092915050565b60018060a01b038816815286602082015285604082015260a06060820152600061484260a083018688614427565b8281036080840152614680818587614427565b60006001600160401b0382111561486e5761486e6140d7565b5060051b60200190565b600082601f83011261488957600080fd5b815161489c61489782614855565b614116565b8082825260208201915060208360051b8601019250858311156148be57600080fd5b602085015b838110156148db5780518352602092830192016148c3565b5095945050505050565b6000806000606084860312156148fa57600080fd5b83516001600160401b0381111561491057600080fd5b8401601f8101861361492157600080fd5b805161492f61489782614855565b8082825260208201915060208360051b85010192508883111561495157600080fd5b6020840193505b82841015614973578351825260209384019390910190614958565b6020880151909650925050506001600160401b0381111561499357600080fd5b61499f86828701614878565b604086015190935090506001600160401b038111156149bd57600080fd5b6149c986828701614878565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, -<<<<<<< HEAD - "start": 9206 + "start": 9492 }, { "length": 20, - "start": 9390 + "start": 9680 }, { "length": 20, - "start": 10790 + "start": 10598 }, { "length": 20, - "start": 14918 + "start": 14906 }, { "length": 20, - "start": 15360 -======= - "start": 8013 - }, - { - "length": 20, - "start": 8201 - }, - { - "length": 20, - "start": 8669 - }, - { - "length": 20, - "start": 12399 - }, - { - "length": 20, - "start": 12850 ->>>>>>> main + "start": 15357 } ] } @@ -1777,52 +1760,28 @@ "PoseidonT3": [ { "length": 20, -<<<<<<< HEAD - "start": 8992 - }, - { - "length": 20, - "start": 9176 - }, - { - "length": 20, - "start": 10576 - }, - { - "length": 20, - "start": 14704 - }, - { - "length": 20, - "start": 15146 -======= - "start": 7796 + "start": 9275 }, { "length": 20, - "start": 7984 + "start": 9463 }, { "length": 20, - "start": 8452 + "start": 10381 }, { "length": 20, - "start": 12182 + "start": 14689 }, { "length": 20, - "start": 12633 ->>>>>>> main + "start": 15140 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", -<<<<<<< HEAD - "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" -======= - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" ->>>>>>> main + "buildInfoId": "solc-0_8_28-b4c3d00ecb469b7feb2a44356615e2767397e101" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json index 349198336..b14557440 100644 --- a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json +++ b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json @@ -1428,11 +1428,7 @@ "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": { -<<<<<<< HEAD - "3415": [ -======= - "4010": [ ->>>>>>> main + "4593": [ { "length": 32, "start": 3120 @@ -1446,71 +1442,43 @@ "start": 5419 } ], -<<<<<<< HEAD - "6684": [ -======= - "7066": [ ->>>>>>> main + "7931": [ { "length": 32, "start": 5651 } ], -<<<<<<< HEAD - "6686": [ -======= - "7068": [ ->>>>>>> main + "7933": [ { "length": 32, "start": 5609 } ], -<<<<<<< HEAD - "6688": [ -======= - "7070": [ ->>>>>>> main + "7935": [ { "length": 32, "start": 5567 } ], -<<<<<<< HEAD - "6690": [ -======= - "7072": [ ->>>>>>> main + "7937": [ { "length": 32, "start": 5732 } ], -<<<<<<< HEAD - "6692": [ -======= - "7074": [ ->>>>>>> main + "7939": [ { "length": 32, "start": 5772 } ], -<<<<<<< HEAD - "6695": [ -======= - "7077": [ ->>>>>>> main + "7942": [ { "length": 32, "start": 6200 } ], -<<<<<<< HEAD - "6698": [ -======= - "7080": [ ->>>>>>> main + "7945": [ { "length": 32, "start": 6245 @@ -1518,9 +1486,5 @@ ] }, "inputSourceName": "project/contracts/token/EnclaveTicketToken.sol", -<<<<<<< HEAD - "buildInfoId": "solc-0_8_28-c510d3f82db7eda6957be691c11709bfbc8fd9ed" -======= - "buildInfoId": "solc-0_8_28-ce8ef221f0c6b7e9da5e81af1e5b728128db3d16" ->>>>>>> main + "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 67275690d..37c7491bc 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -30,6 +30,7 @@ import { * @notice Ownable implementation of the ciphernode registry with IMT-based membership tracking * @dev Manages ciphernode registration, committee selection, and integrates with bonding registry */ +// solhint-disable-next-line max-states-count contract CiphernodeRegistryOwnable is ICiphernodeRegistry, Ownable2StepUpgradeable @@ -135,9 +136,9 @@ contract CiphernodeRegistryOwnable is uint256 public pendingDkgFoldAttestationVerifierAt; /// @notice DKG anchor commitments stored when the committee public key is published. - mapping(uint256 e3Id => uint256[]) internal dkgPartyIds; - mapping(uint256 e3Id => bytes32[]) internal dkgSkAggCommits; - mapping(uint256 e3Id => bytes32[]) internal dkgEsmAggCommits; + mapping(uint256 e3Id => uint256[] partyIds) internal dkgPartyIds; + mapping(uint256 e3Id => bytes32[] skAggCommits) internal dkgSkAggCommits; + mapping(uint256 e3Id => bytes32[] esmAggCommits) internal dkgEsmAggCommits; //////////////////////////////////////////////////////////// // // diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 359a9d0a9..231dbd7d2 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -204,7 +204,6 @@ describe("Enclave", function () { }; }; - describe("constructor / initialize()", function () { it("correctly sets owner", async function () { const { enclave, owner } = await loadFixture(setup); diff --git a/packages/enclave-contracts/test/Pricing/DustRotation.spec.ts b/packages/enclave-contracts/test/Pricing/DustRotation.spec.ts index 53d258535..8857bf789 100644 --- a/packages/enclave-contracts/test/Pricing/DustRotation.spec.ts +++ b/packages/enclave-contracts/test/Pricing/DustRotation.spec.ts @@ -37,7 +37,7 @@ describe("Pricing — per-E3 dust rotation across consecutive E3s", function () await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x", "0x"); }; const setup = async () => { @@ -128,10 +128,11 @@ describe("Pricing — per-E3 dust rotation across consecutive E3s", function () }; await feeToken.approve(await enclave.getAddress(), ethers.MaxUint256); await enclave.request(req); + // topNodes are sorted by ascending address; operator3 < operator1 < operator2 const nodes = [ + await operator3.getAddress(), await operator1.getAddress(), await operator2.getAddress(), - await operator3.getAddress(), ]; await setupAndPublishCommittee( ciphernodeRegistryContract, diff --git a/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts b/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts index ceb860677..159288578 100644 --- a/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts +++ b/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts @@ -32,7 +32,7 @@ describe("Enclave — pull payments + fee-token allow-list", function () { await time.increase(SORTITION_SUBMISSION_WINDOW + 1); await registry.finalizeCommittee(e3Id); const pkCommitment = ethers.keccak256(publicKey); - await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x"); + await registry.publishCommittee(e3Id, publicKey, pkCommitment, "0x", "0x"); }; // Two fixtures: one using vanilla USDC (allow-list tests), diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index dccc53a00..201f754a3 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -235,15 +235,17 @@ describe("CiphernodeRegistryOwnable", function () { await finalizeCommitteeAfterWindow(registry, 0); await expect( - registry.connect(notTheOwner).publishCommittee(0, data, dataHash, "0x", "0x"), + registry + .connect(notTheOwner) + .publishCommittee(0, data, dataHash, "0x", "0x"), ) .to.emit(registry, "CommitteePublished") .withArgs( 0, [ + await operator3.getAddress(), await operator1.getAddress(), await operator2.getAddress(), - await operator3.getAddress(), ], data, dataHash, @@ -300,14 +302,16 @@ describe("CiphernodeRegistryOwnable", function () { await registry.connect(operator3).submitTicket(0, 1); await finalizeCommitteeAfterWindow(registry, 0); - await expect(await registry.publishCommittee(0, data, dataHash, "0x", "0x")) + await expect( + await registry.publishCommittee(0, data, dataHash, "0x", "0x"), + ) .to.emit(registry, "CommitteePublished") .withArgs( 0, [ + await operator3.getAddress(), await operator1.getAddress(), await operator2.getAddress(), - await operator3.getAddress(), ], data, dataHash, diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index a17ee8eca..692239315 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -353,8 +353,6 @@ describe("Committee Expulsion & Fault Tolerance", function () { 0, 31337, ethers.keccak256(evidence1), - undefined, - evidence1, ); await slashingManager.proposeSlash( 0, @@ -426,8 +424,6 @@ describe("Committee Expulsion & Fault Tolerance", function () { 0, 31337, ethers.keccak256(ev1), - undefined, - ev1, ); await slashingManager.proposeSlash( 0, @@ -448,8 +444,6 @@ describe("Committee Expulsion & Fault Tolerance", function () { 7, // C6ThresholdShareDecryption — different proofType 31337, ethers.keccak256(ev2), - undefined, - ev2, ); await slashingManager.proposeSlash( 0, @@ -567,8 +561,6 @@ describe("Committee Expulsion & Fault Tolerance", function () { 0, 31337, ethers.keccak256(evExpel1), - undefined, - evExpel1, ); await slashingManager.proposeSlash( 0, @@ -586,8 +578,6 @@ describe("Committee Expulsion & Fault Tolerance", function () { 0, 31337, ethers.keccak256(evExpel2), - undefined, - evExpel2, ); await slashingManager.proposeSlash( 0, @@ -721,8 +711,6 @@ describe("Committee Expulsion & Fault Tolerance", function () { 0, 31337, ethers.keccak256(evExpelOp1), - undefined, - evExpelOp1, ); await slashingManager.proposeSlash( 0, @@ -740,8 +728,6 @@ describe("Committee Expulsion & Fault Tolerance", function () { 0, 31337, ethers.keccak256(evExpelOp2), - undefined, - evExpelOp2, ); await slashingManager.proposeSlash( 0, diff --git a/packages/enclave-contracts/test/fixtures/helpers.ts b/packages/enclave-contracts/test/fixtures/helpers.ts index 609dfbf97..f0667be84 100644 --- a/packages/enclave-contracts/test/fixtures/helpers.ts +++ b/packages/enclave-contracts/test/fixtures/helpers.ts @@ -39,6 +39,7 @@ export const setupAndPublishCommittee = async ( publicKey: string, operators: Signer[], committeeProof: string = "0x", + dkgAttestationBundle: string = "0x", ): Promise => { for (const operator of operators) { await registry.connect(operator).submitTicket(e3Id, 1); @@ -51,6 +52,7 @@ export const setupAndPublishCommittee = async ( publicKey, pkCommitment, committeeProof, + dkgAttestationBundle, ); }; From 8a841717468169df094b7316e57e1008d4ba34b0 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Sat, 23 May 2026 11:06:00 +0500 Subject: [PATCH 39/54] fix: update addresses --- examples/CRISP/enclave.config.yaml | 4 +- .../crisp-contracts/deployed_contracts.json | 44 ++++++++++--------- examples/CRISP/server/.env.example | 2 +- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index 9c93b70fd..3b681cc95 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -5,8 +5,8 @@ chains: rpc_url: ws://localhost:8545 contracts: e3_program: - address: "0x1429859428C0aBc9C2C47C8Ee9FBaf82cFA0F20f" - deploy_block: 43 + address: "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0" + deploy_block: 38 enclave: address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" deploy_block: 14 diff --git a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json index bc1c3c072..f4bd486dd 100644 --- a/examples/CRISP/packages/crisp-contracts/deployed_contracts.json +++ b/examples/CRISP/packages/crisp-contracts/deployed_contracts.json @@ -234,7 +234,7 @@ "initialOwner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "proxyAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", "proxyAdminAddress": "0x1F708C24a0D3A740cD47cC0444E9480899f3dA7D", - "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" + "implementationAddress": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "blockNumber": 14, "address": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" @@ -256,40 +256,42 @@ "address": "0x9A676e781A523b5d0C0e43731313A708CB607508" }, "MockComputeProvider": { - "blockNumber": 17, - "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" + "blockNumber": 30, + "address": "0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00" }, "MockDecryptionVerifier": { - "blockNumber": 18, - "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" + "blockNumber": 31, + "address": "0x36C02dA8a0983159322a80FFE9F24b1acfF8B570" }, "MockPkVerifier": { - "blockNumber": 19, - "address": "0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" + "blockNumber": 32, + "address": "0x809d550fca64d94Bd9F66E60752A544199cfAC3D" }, "MockE3Program": { - "blockNumber": 20, - "address": "0x851356ae760d987E095750cCeb3bC6014560891C" - }, - "ZKTranscriptLib": { - "blockNumber": 22, - "address": "0x95401dc811bb5740090279Ba06cfA8fcF6113778" + "blockNumber": 33, + "address": "0x4c5859f0F772848b2D91F1D83E2Fe57935348029" }, "MockRISC0Verifier": { - "address": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf", - "blockNumber": 26 + "address": "0xCD8a1C3ba11CF5ECfa6267617243239504a98d90", + "blockNumber": 37 }, "HonkVerifier": { - "blockNumber": 23, - "address": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf" + "address": "0x2bdCC0de6bE1f7D2ee689a0342D76F52E8EFABa3", + "blockNumber": 38 }, "CRISPProgram": { - "blockNumber": 25, - "address": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF" + "address": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + "blockNumber": 38, + "constructorArgs": { + "enclave": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", + "verifierAddress": "0xCD8a1C3ba11CF5ECfa6267617243239504a98d90", + "honkVerifierAddress": "0x2bdCC0de6bE1f7D2ee689a0342D76F52E8EFABa3", + "imageId": "0x23734b77b0f76e85623a88d7a82f24c34c94834f2501964ea123b7a2027013a2" + } }, "MockVotingToken": { - "blockNumber": 26, - "address": "0x9d4454B023096f34B160D6B654540c56A1F81688" + "address": "0xc351628EB244ec633d5f21fBD6621e1a683B1181", + "blockNumber": 40 } } } \ No newline at end of file diff --git a/examples/CRISP/server/.env.example b/examples/CRISP/server/.env.example index bbeb57abd..eb7ccf8bf 100644 --- a/examples/CRISP/server/.env.example +++ b/examples/CRISP/server/.env.example @@ -17,7 +17,7 @@ CRON_API_KEY=1234567890 # Stale E3_PROGRAM_ADDRESS causes requestE3 to revert with empty data `0x`. ENCLAVE_ADDRESS=0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e FEE_TOKEN_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 -E3_PROGRAM_ADDRESS=0x9d4454B023096f34B160D6B654540c56A1F81688 +E3_PROGRAM_ADDRESS=0x7969c5eD335650692Bc04293B07F5BF2e7A673C0 CIPHERNODE_REGISTRY_ADDRESS=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 # CRISP voting eligibility token (MockVotingToken) — NOT the fee token above CRISP_VOTING_TOKEN= From b0fe52c7cc8e626f90aebe732fcc7a3d51a638c2 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sat, 23 May 2026 13:22:50 +0200 Subject: [PATCH 40/54] avoid dkg verifier to be passed, fix resolutions from merge --- Cargo.lock | 1 + .../benchmark_run_meta.json | 2 +- .../crisp_verify_gas.json | 96 +-- .../integration_summary.json | 128 ++-- .../benchmarks/results_insecure_agg/report.md | 144 ++--- .../benchmark_run_meta.json | 2 +- .../crisp_verify_gas.json | 58 +- .../integration_summary.json | 84 +-- .../results_insecure_no_agg/report.md | 40 +- .../scripts/extract_crisp_verify_gas.sh | 4 +- circuits/benchmarks/scripts/run_benchmarks.sh | 2 + .../src/ciphernode_builder.rs | 167 ++++- crates/config/src/app_config.rs | 42 ++ crates/config/src/contract.rs | 5 - crates/entrypoint/src/start/start.rs | 13 +- .../accusation_quorum_reached.rs | 29 +- .../src/enclave_event/accusation_vote.rs | 53 +- .../enclave_event/proof_failure_accusation.rs | 5 + crates/evm/src/ciphernode_registry_sol.rs | 48 ++ crates/evm/src/lib.rs | 5 +- crates/evm/src/slashing_manager_sol_writer.rs | 57 +- crates/multithread/src/multithread.rs | 8 +- crates/test-helpers/src/lib.rs | 9 + crates/test-helpers/src/usecase_helpers.rs | 44 +- crates/tests/tests/integration.rs | 56 +- crates/trbfv/src/gen_esi_sss.rs | 9 +- crates/trbfv/src/gen_pk_share_and_sk_sss.rs | 23 +- crates/zk-prover/Cargo.toml | 1 + .../src/actors/accusation_manager.rs | 580 +++++++++++++----- .../src/actors/accusation_manager_ext.rs | 8 + crates/zk-prover/src/actors/mod.rs | 4 +- .../src/actors/node_proof_aggregator.rs | 2 +- crates/zk-prover/src/actors/proof_request.rs | 6 +- .../src/actors/proof_verification.rs | 8 +- crates/zk-prover/src/prover.rs | 22 +- .../tests/slashing_integration_tests.rs | 350 ++++++++--- docs/pages/internals/dkg.mdx | 43 ++ examples/CRISP/enclave.config.yaml | 3 - .../packages/crisp-contracts/deploy/deploy.ts | 1 - .../contracts/Enclave.sol/Enclave.json | 2 +- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 15 +- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../ISlashingManager.json | 2 +- .../CiphernodeRegistryOwnable.json | 78 ++- .../EnclaveTicketToken.json | 2 +- .../interfaces/ICiphernodeRegistry.sol | 4 + .../registry/CiphernodeRegistryOwnable.sol | 58 ++ .../contracts/slashing/SlashingManager.sol | 21 +- .../scripts/benchmarkGasFromRaw.ts | 27 + .../bfv_vk_binding/folded_artifacts.json | 8 +- templates/default/enclave.config.yaml | 6 + tests/integration/enclave.config.yaml | 3 - 53 files changed, 1672 insertions(+), 720 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 14903ce4d..48b8666de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3938,6 +3938,7 @@ dependencies = [ "e3-config", "e3-data", "e3-events", + "e3-evm", "e3-fhe-params", "e3-polynomial", "e3-request", diff --git a/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json b/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json index 8a09e87e9..fb21f2bcb 100644 --- a/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json +++ b/circuits/benchmarks/results_insecure_agg/benchmark_run_meta.json @@ -3,7 +3,7 @@ "bfv_preset_subdir": "insecure-512", "proof_aggregation": true, "multithread_jobs": 13, - "verbose": false, + "verbose": true, "nodes_spawned": 20, "committee_size_n": 3, "network_model": "in_process_bus", diff --git a/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json b/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json index 7edc06ea4..718713366 100644 --- a/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure_agg/crisp_verify_gas.json @@ -1,8 +1,8 @@ { "verify_gas": { - "dkg": 3042548, - "user": 2973097, - "dec": 3553726 + "dkg": 3125294, + "user": 2972881, + "dec": 3640985 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,14 +17,14 @@ }, "calldata_gas": { "dkg": { - "proof": 170028, + "proof": 169980, "public_inputs": 6168, - "total": 176196 + "total": 176148 }, "dec": { - "proof": 169992, - "public_inputs": 17316, - "total": 187308 + "proof": 169956, + "public_inputs": 17304, + "total": 187260 } }, "integration_summary": { @@ -47,55 +47,55 @@ "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.124654569, "runs": 3, "total_seconds": 0.373963707 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.625217639, "runs": 3, "total_seconds": 1.875652917 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.556127167, "runs": 1, "total_seconds": 0.556127167 }, - { "name": "GenEsiSss", "avg_seconds": 0.166924514, "runs": 3, "total_seconds": 0.500773542 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.241305361, "runs": 3, "total_seconds": 0.723916083 }, - { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 7.62245093, "runs": 3, "total_seconds": 22.867352791 }, - { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 35.477445166, "runs": 3, "total_seconds": 106.432335499 }, - { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 7.549333777, "runs": 3, "total_seconds": 22.648001333 }, - { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 35.646490666, "runs": 3, "total_seconds": 106.939472 }, - { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 7.196253763, "runs": 3, "total_seconds": 21.588761291 }, - { "name": "NodeDkgFold/node_fold", "avg_seconds": 17.672638027, "runs": 3, "total_seconds": 53.017914083 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.409093792, "runs": 1, "total_seconds": 8.409093792 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 49.418440542, "runs": 1, "total_seconds": 49.418440542 }, - { "name": "ZkDkgAggregation", "avg_seconds": 20.366319709, "runs": 1, "total_seconds": 20.366319709 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 3.170409645, "runs": 6, "total_seconds": 19.022457874 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 111.173713722, "runs": 3, "total_seconds": 333.521141167 }, - { "name": "ZkPkAggregation", "avg_seconds": 4.325023125, "runs": 1, "total_seconds": 4.325023125 }, - { "name": "ZkPkBfv", "avg_seconds": 0.433745833, "runs": 3, "total_seconds": 1.3012375 }, - { "name": "ZkPkGeneration", "avg_seconds": 5.172915375, "runs": 3, "total_seconds": 15.518746125 }, - { "name": "ZkShareComputation", "avg_seconds": 7.412439382, "runs": 6, "total_seconds": 44.474636292 }, - { "name": "ZkShareEncryption", "avg_seconds": 7.468529083, "runs": 24, "total_seconds": 179.244698 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 7.677126833, "runs": 3, "total_seconds": 23.031380501 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.114853486, "runs": 3, "total_seconds": 0.344560458 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.24184635, "runs": 5, "total_seconds": 1.20923175 } + { "name": "CalculateDecryptionKey", "avg_seconds": 0.004296347, "runs": 3, "total_seconds": 0.012889042 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.020525153, "runs": 3, "total_seconds": 0.061575459 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.019100917, "runs": 1, "total_seconds": 0.019100917 }, + { "name": "GenEsiSss", "avg_seconds": 0.006925194, "runs": 3, "total_seconds": 0.020775584 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.010452847, "runs": 3, "total_seconds": 0.031358542 }, + { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 8.270900388, "runs": 3, "total_seconds": 24.812701166 }, + { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 34.927441903, "runs": 3, "total_seconds": 104.782325709 }, + { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 7.542320708, "runs": 3, "total_seconds": 22.626962125 }, + { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 34.922820125, "runs": 3, "total_seconds": 104.768460375 }, + { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 7.895315874, "runs": 3, "total_seconds": 23.685947624 }, + { "name": "NodeDkgFold/node_fold", "avg_seconds": 18.310982819, "runs": 3, "total_seconds": 54.932948458 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 1.566955375, "runs": 1, "total_seconds": 1.566955375 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 47.874825458, "runs": 1, "total_seconds": 47.874825458 }, + { "name": "ZkDkgAggregation", "avg_seconds": 19.861393583, "runs": 1, "total_seconds": 19.861393583 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 1.358248069, "runs": 6, "total_seconds": 8.149488419 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 111.87172225, "runs": 3, "total_seconds": 335.61516675 }, + { "name": "ZkPkAggregation", "avg_seconds": 0.925223417, "runs": 1, "total_seconds": 0.925223417 }, + { "name": "ZkPkBfv", "avg_seconds": 0.218732388, "runs": 3, "total_seconds": 0.656197166 }, + { "name": "ZkPkGeneration", "avg_seconds": 3.515460263, "runs": 3, "total_seconds": 10.54638079 }, + { "name": "ZkShareComputation", "avg_seconds": 2.332301763, "runs": 6, "total_seconds": 13.993810582 }, + { "name": "ZkShareEncryption", "avg_seconds": 3.965180039, "runs": 24, "total_seconds": 95.164320958 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 3.440710889, "runs": 3, "total_seconds": 10.322132667 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.103301764, "runs": 3, "total_seconds": 0.309905292 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.285312216, "runs": 5, "total_seconds": 1.426561084 } ], - "operation_timings_total_seconds": 1037.711237248, + "operation_timings_total_seconds": 882.167406542, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, - { "label": "Setup completed", "seconds": 3.042068, "metric": "wall_clock" }, - { "label": "Committee Setup Completed", "seconds": 20.254953375, "metric": "wall_clock" }, - { "label": "Committee Finalization Complete", "seconds": 0.005661959, "metric": "wall_clock" }, - { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 131.583918, "metric": "wall_clock" }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 159.570454958, "metric": "wall_clock" }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 162.126205083, "metric": "wall_clock" }, - { "label": "Application CT Gen", "seconds": 0.3074025, "metric": "wall_clock" }, - { "label": "Running FHE Application", "seconds": 0.003480958, "metric": "wall_clock" }, - { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 57.874201, "metric": "wall_clock" }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 68.103220875, "metric": "wall_clock" }, - { "label": "Entire Test", "seconds": 253.849896667, "metric": "wall_clock" } + { "label": "Setup completed", "seconds": 2.7038335, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.174264042, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.003923875, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 131.619168, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 144.272573833, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 144.78216475, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.009741125, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.000051333, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 49.457601, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 53.35583, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 221.027911708, "metric": "wall_clock" } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000ca0bfc9448e9ef16c000000000000000000000000000000000000000000000002c6fc73ad221ed124000000000000000000000000000000000000000000000008224c7cdccbb18af40000000000000000000000000000000000000000000000000001877d7b0fe868000000000000000000000000000000000000000000000004ffaa9844e438d4d300000000000000000000000000000000000000000000000ac1d3924637ac840700000000000000000000000000000000000000000000000b23147b92159485ad0000000000000000000000000000000000000000000000000002e033f6bc040300000000000000000000000000000000000000000000000768068eda5eb7ec45000000000000000000000000000000000000000000000006b320ac7697a7d933000000000000000000000000000000000000000000000003ca3c43312f4a1a730000000000000000000000000000000000000000000000000000b90e9ebd4813000000000000000000000000000000000000000000000002d7efb8bc1720f5e4000000000000000000000000000000000000000000000003f44e6bbb5f7dd5e000000000000000000000000000000000000000000000000d73b5021fc0617d170000000000000000000000000000000000000000000000000002138f4eea500814ddc04dff7aade6906eaa66607c4f4fc4b7fad8d7d6dc1781b1ccb5824ae1460e35805f5cf672d03d0c6e86292f9270e308666e0a68e54ba34c48c7d1c1171e08ab78f3b3a380edceaf2be1332bb0f6970b24f889216fb52b96903c3672078e2a2d8c18c4b84f1ee541265621917acd4fce05eec99dc817ef9a1b30df545bdf2ffe8839de28e724ff17364a02a9190034ee4093d0771fe513866af5eee3e16c2640fd2eed50030bcc39cdfc2089b3af2b2fc2cf47c558c544d264cb316924fb06cabc1c64e480876c03a96dd26e2ec80e2a1fe4a060eba855017ac571e27bf226f2d314aa75307227826f8c8cc3558237c0c85427b4053190b5a66b8518499a04c8896e47bde11d94d474f7eb7e24a697ff050a323e51605c0a2f3795b43a55216ecd8b7dbdd337ec0eed71fac7dca88403ce3aad06ca8e1f5804dbf2dbfeea2d01dd0ea69cbd98b44cd4163166c47696b0cb64815f84169ca91ff184bc2aa703873167206ba8215672551c6c39711972e0fcb6171286004706f3f61d7c56d50f286c3eb1632be8786c44dc777090a9f3b2528a91259674a958b8287d9e6e7902eda561c95e35960e5fc1a918c47214a8a3fd83999188e9cf8c4bb3af86a867072be6dc0e4cf33e535bb85259a027f564bfcb50f2633630222848a6fbc30bc81f15de7cce451611df563628ebf63c866506617a24c47ee9943936bedf6006d403e517ea950c046b99c76b6a2f049de41634d85d23ec7fe432a42c24131a81e41e87bc342ff0f4a94a49a0e42bc24fab0ad8569dbe45de50452d2f1518f6964d106282a56209e9549419539fa23a7a21d5adb558af735bb87d547053b4b077b328e1943bbf60c5f59f31a56dbd1bf144efa369f33bb0c50f7e45ebc416e850d2241b4c910649052f98c615c093e763eb742f1cd429f36c4824b3f875f18c45581825b2a7c498228972f28cc96b5c90ee1c1dbbbc46d9ffae4973bccf711fca6d17a07d52ec5c6aed0fc3e9a064c37ade09de5dcae4e637f46271f943e8933c50061892e9ad08e8bff03cd7eddfc9ffada674bb27d9867e628d90ef6e9fd19ee92222e6a3072e0948ff4d178389a49a145e68efdbf306e35874e3ee27452ff17028fbcba83976dfd49e75c8bad82fb9339e77aa7fb76af7670b90e084210ebcc223d988bb703c4a113f7131e52a583d944668b72c08fcc4f552a6927dc3c7333b06b41b4b298fb5caa9170db380fe81195d0da8c38d6de7c337c52e5f024f046526d3b8bf4befc00cc172fde6e835ebe761c7af500c1a3de202df707e91201f7a02d38fa8b9456f6777eb006c1834afae72b3e500ffcea21a36b82e83a704da1e1863aaf8b58a8662f0dcb6fe6fd3e35bcb4bb922291315bcdd7d2f00ccc7db3d20e4d6112826184c37f83fef8bbe495aa3976e2e8c334b967ce4fc0a94d26503189d92a2f7b89fea1bcef0c5ae1147d833e4ac1d52120b36cb7d10619527140b27a521e78a3d4075e127c21300306256edc76d094502551553efcead0caec3b1097621f0a820ca04263bd57e7afa49cfb92a50eeffe2ac26f7502a496adfdf8e23d183479522e4fd838ee09a8fd50fec4a22103536fb118a251999b16042603d025ddd4670df746903205ec04aaa1c131a8565298996d2ef8ae601c995ee0e1c27b2ee13e4555206067c20ee5e054c793933fad112f9cfb4edefa8b6a5cccc8a01fdf4cb41e6d9bf4216246445428daa66e8263a65b79139ab1011ef9a150b9b1a40b8798c1e4e008ec02795bbf95bdd7a531a94ae8df852e92b98e7766fa3ad0c79d387b21791f763ca553d7d225632bcf58e60c6c608ee1443f98f979f9d3603afa7aef8fb5c103ca78c01bcc6917d4f1e0aa09bf3ea417c51c1c3f9ea2b902151931b21c9d25e574019fb092e03c40399b0b8fad6e3347f5961674c366450049609cb8eae6ff4e987806a36851ec132675dfdd390f61fc185b808b31af6ab220359a2710339887cdb032a26cee0dc574e33c1651f7e8b746d016f39fff2c605cb5e7f0b7a839277f904d2bf506ecc02f74be5baa88793d3265700bf8801312174144bf5372435bebb6c3af3d31bd15519858190765d99eb03caa1b516aa3a219db366834e6cf2d8dd47bafc50fce17f23a9556dc30699f727f09edecee94d252a7c4d5498c27a3915b7ac677625ac6829b5777a2dd234491d87168bccbf5301b9221ff82f1648b3b24d2e107329c0a910034296426f76ccc9941c1267df6d14d180edf7ff58f8aafc9fddc2b6a9a641e608c5d6ee03ccd615b645b17775502ac1b479e0010f878c96dcfb913639debcc8dc163faa63994d6fe4299db056f125cbabf7fd62bc792e6505b14e5849ef4506bb43babd4cd46e9df6d402c7e4de2e213bf9ca80aaca06b1baeb5554cce579df7cfad676200cfd2518592c92c6810728dcbf5a308018bdd6a9d0d6a3249cd59a8725da15de0a96a899f510c5407711b0753955e733c32aa3b780c292dcc4a8a24ff2f7bb224370ad0b0c6fca1f601947e99c3cbb54eefe69676eb12aaa3473410966221a4980511010e6f3212c281aae2b27f42ea11fb627fa4c854828ae3d1bb27551fb5ec75b54e342f5cef48f0d592aca51491f88b890e7dd0aaeeb290d165a763aa3b7f3b34ff65fa5a05f8f014f6b3daab5bcefdc03d0aa1f38464f5b0791d8e1f379669b72f548d9afe3490cbeeb0626f19b784265812c1f6161978d27f9e9c0349f028fe3b71197ef130c2dcf2eb7d5edb0b18a4194616eb35c7d614e26fcab690613d9757b323d94867d0da5034a534f65584cce55bd9c5119c20d2b8431ace83236e90ccb7be0c387b11b3c916229a3bd8603bfcb9e293b7c9a2363b898382d57eed13d5ed795066b07218dab59a3ffb999172a0025c43d200d12d8a155128c4db831c24560f654e43f1a27266db9ad1d94700efab02e2f0d39c6e2a84f02523fb12d290ba50e9a1a9805b38dca8bc7363b838f11fc5fcc4a39acc6c72112807ce045be538f43e9f27c01e75d05c14f7596940ec764feb95e2e28f10390a0e74e5cb3f16140127cfa6a156fa560a3c6e13592c8cb5acce08db1c9874a24e8a9ec69e453fa9f1384d46b1f263787814072fc03ea72b8abc134a205c058e5e9b963cf4b4b043993a01abe01f4245be8b9d7f9f127e5c248ac6c5b5dd7b3454e07ded8374360d40d75a39d1d609f99c2bcec53998e407d157b88698bf2b4c830874dfa537b5660303e798d095fe35d6c3ccbbbd131305128dfc78d99bd02faaca0f03250b0ba2cf8f859000afa115aacb761780b4ccf0eabb203038ea0ba410b2444e91ae6a10d0a729763234c408b6cbaf328d74d6be6bfdd4b4bdb74bde0e49da02911bbac860f7b1ecd27981f9ba1b18d35d4db16012579d470cba0e5e117421ac0da980fb45ea23f9b1f4bfded7640d9a41767a3819ffed3d0b1ccff98ad78f63cb4548b6c487e3fa90a8e68436d8db97480afb645de0e76939f6aec3a9183ccc113ae639c4f445b4c161e47279af3abe35b80d6a971ae876e0bcdf15ddc455da007ed56c493de38182b07e1f76681a2fa9196f45c3d25f98606688b55ea6336e19638711ff00d788f16bab3baf7545061ef03f59fd3b3c69a5da63683b801868689aa56c890cba7640b3ef1806aae00be03157081b188c4bfcc045ddd65d1364bbd2f73d03d5eccff18cbfd02fef55f8259a3abb1cf3c80dae34193e155ce2d02117b2281181acad525abfcef8f617e08f3264d0cc3676b330037bef0c76d2be781d9109130dae3a2064dde40ea9103327dc858625e5c5f43636aeadfbbf08b766607aea64758115d12f3a3ccaa680c8bac7c90675b26db2aa4e437820f8843f613f9f1bfd8cb4a431bdab7ae3f2815251e6440f4575397b1362413dc0d908ac8035d7e0dc154462102269ce752caf1c0e965c80463937edade5f71dbb16559824a082fd53c65483e06fb749bec6adfe9659772b8db6d26541bf074d751096ad81e99c52d7ef4102c2646c0a220aa8b25cc7fff384b029e841c5e1d1a9663e247c9fb35838a44322b00b86c4f53843909921594d8772dd67f6fcd57b555453ec0e6a2914fb82b4f9e2fa795c99591a02f6cddcbeeff3995a26eff6d038b7c063ce6b7e95f591130710c9a83eb1d4092d30b6e3a0db2643d049eddbb6486ca3ff613045343ae99a6af052b868c84142535fcf42cd31d9c039d599e1492cce99d4dd8439330b0a2077f165fc7e1fb0a27e24eac75faefb6ebb8b825e535423f5663b09f7b157205c0302151f296af0b1d5734e1e2a8eb3cd397a8f7e9cd015e333ce73c33df4bd49e860bda526af203f82dc6b615c2ebbbaa3b99a13e45f231c133bdc755807b03311123d395bfe1a13422c38063cd2f8cc6604a3ffbad19e5bbd5776668f5b907c3f72150da121fc376b7231173de23fb4b6d2edc5989dd2e82ae695cce929259aac229968d3d266b9913cf2d577474ccbda946ddc4ad52cab0388806f364246982402ca8b651f56a0f593424f4511a776152de94b2d5e040f23eb6f50d03029a0731056d6bb00954510884059b04e580005f131b335f45dd627242e573447d24ca9317ebaa00cee1d6cd54e10693d4656d19191ea9d63f1fb58989b317a7bb4f173f02154a9ad7593c3f48daa36a36deaf60a363365cadefcb3cd7f7eedf78e07817168d8e60ae9532d27b0eb422367876aa2840738a06d8487727fb74a6e75e44f6045b19a874d8bbd97efbaf376dd343648da57e8da19a975e3c715af62bcd12cf1b40e16223afd640866cefb3d7dba53e2e6a306634692d558fdf6ca8190683c8241d67526488d47450901eee756a9ce807cf18f54e62cc823f5d47b4e3af46ca1f3b94e88bd4115fd91451db59a8e8dc0c111024a2b38af1deb3854d2994887b1037817024ab3081acfeff6b50282ba402d85b56966899fb39a6c01df6771f33044b41a29308e89a75b06b6627d85cb028d191f46bfc52df2723360f52662e99087a5737c61ee99f7cd057bf2e18689f5b747595ff1199ae6798b0499c8d75e113362ac3d589042b46807e6fbf16b6e40c8f94e444d7652dcc44d8984ad500cf1d29a2242bd30bfb5bd3eb76e7cf673275c21f9ecd128d346760169d28ab130b042b4111792bb700688ed4c313c5f168776bebd4efc7b5c8a244d8ba035bc42d039278015f319ef204e7869582a42a617cc51566356ba50ed425e8a4b749d81a0ad6f18abb1f53267cff6f65e6714c4a397efb80184e037c6327175f09bcaf7e28e8c9aee1ff608389deb8627134d0ce9fa5feb55c170e782d5214ccd605eec1257df978c84c72adec12e2b0f3126371d1ee637532aa455e2d2a78d62afdd7180045f5626941bdf1f7849adbcb24000d1c9eac6d91adadf387706d72b5dd34e804d4b52193f0dcd0d219a9063743deb5f4349336dbe6b49dbf416c1789d3d07d304e7354d6a54ead2ec2ba06b67b345473b6e1a9b1c26156c39facaf76f899e428ccb16ec1924cb31fb117f22bd7b131cbd6ea732abfc50459469e76d07dd90f1387549c3b417078677264bf1f284a099584aac71c07e7fb7363c60c868f435d20a32b528b59ce742f5afae859176c4931b55e6c0fd7e3b34fac1e1fc4116fb61da0d8cf4cb32d83588556adfc358e1130ce98576c3a350612bb36da466e5e430cd2ee1202e4ba044375f6bb5082a688a43cbc32fa463585c9f90a8572910d9a06c16854bbd5268dc754d94d49f89542e8cc8b2ef418ceb21cac5bd74938c7fa1da3952a91f4563cfccc6eb1946a3ddf30c389085a4ab95a232b732e139e86200d1a9770453f3cf095f35f9ddaea29d99de184b8123a5f2b63c0e824c16207991ad29daaa0ee898629c92a646495916aa6fd424d5015b85432cee703ad1b2aeb038313c5e1410d79237e6ca180f40c19d5e5b2513a1377ca2fac6715217ba9351fc5b3942d314b2e93c71a2f96d9155db47b6d78e1cc2bd004aa22003e163b6412a752ca2e7889cefa2751358fd0fbe3884d99f106d68c973c1dce717a0f33eb02837031a40957b5fa4835b44747471801e52586ac30295db9fc1bb1a704f18b0d1447924bf9f1e02784cc1b0532824a4c078107acd6fd9db620fd1a61a0b0ee0aaa2950bab91529b7454b666079592dc6e88fae0ed90f90d35892225d47a4862f8963dea3b555a8f01f4da08bd46ae1ae6bc16bcf7005347c8c19de6c2145ad125aa502df4cd04faff782bc862e046cc3240bf2c8bcd04f70bb4ef6d36fa27c1677f6bd1cd6b98983ddf5012b2f031398fbf6ca5c8a32da32cde2e62c75369011f86008c2d4c0ca000b7ad4499d797d6b91fa77825fb44045e75a9216249177301422c032ca94ed2749e37ac341bfcd3d25fc7ecd745e66051a06188f3f4a0b07383c49c3ef429d01566aa613cf191134cf3431342729cfbaa7732cf5482fd72aa56ab908372599856c78c0b74632d5d60966806bd67d0f82f96c58df6256021c260bcdf988a10981f18558f30407b89644a7ea76d78c0ddb151d55530b667c1343edabed74cd778cefe84306c1eab5bfebc5d97b7791300950279dc2aeae2f235b102f73827cdd964704d11d5240f22d7da11fa6afa07b841291a2c1a7ac492c757990536725bda41c4083e8acb511abb6eddeecf6e50539c4c011b3d1d10a27f1b8b43e24e1a85a932f807aae6be08145cb67c932c8a20452d5cdec07bb06169254ee048b3edeadb4c9934efcf8527debbf850f8903614da37e81f52853002fd4cfb00556968925d6b81c10197dae197b9ccfece01494971e15967e157e662a022c3517eba2f66934d09cf0610075120f622acdcec3064a52150f6d095a2724e0d64d5824673d815ea1dec20dbb5b193705c78f2b4415be9e5c2ce67e2d1d0846cb6a25cf5de0808ee27abc17f5508b07f104a3f3f0522b6b226f4b7b8b8e0e999218e897f2a6d36fb1f2dbb1e4b57ce685332a56be535c6c6cc5d257e9ff05a455f3cd0d1f8ea76967807a251b0bcb3a1cd8da51868169fc2f727c528545242850473b17de1e572da6cc3796b98847e836d80bf2ec2618287d37d544866004e27ac002f00d5ff8399f2f6c541c3afde128584ec229ff1bfd92b8fb2fedc826a12118800da2ff24c92d5f7d15f409cc23b8cccef62cade5ec8af6270aaac10e4c6b05214f6fb1f74325f1ea5d67238b2789d21a634c24e687a7636b7ce8590d2c3adb23cd6404145afddbf5e18654b398ac0a0ee354b6cfd254d9971412c302b57afb73e644896ec74bdaad3a21c268c48b37a0cf7970a22e14a95f72addf13af72ee85e567fb98daa6d94ba88d6c8af8e02bfc037fe0624c438793677591146cd48b3da4d2330c7cc82be58818b5c96ea094be4a314c8c1c3023340d48f417fdb66ff71bf86748e95ea77dfdcd27d5593241e172d745dd8ca6907f47e6b207103a1fc9bff0ba9cc5556127307bab4dd8a9cd71f62b579ae35854b736af3d2e7648230877b9611da0c0e657ffd48e0e7982e1489b6a9a37943e6cb947bd2e0abacc109c9e01f390f28eb2bc2264e2e865c46662734ab8a718d3d812c8fdfc04357462790a6d2f5ad09b11f9c4954bb8a61e7f7176ffae8ddb6733acd31fad0b5087afb2695f9eaea944cfba9505200d4fe8788a724e4aa60b165b54c35cfc2ea275976325c84249e203d793ace9ad8001a255ebfae02f564a79ad3ef9946c2dde6205a2c3b0fa8cd8e0b431bd3b0ff968a9a97cb61f2737fb6ef216ac4afb121d720587325db079dc26956af3712406d0ce60be27365339434aa7b9ee129001b0515f27e5434b9ecf0e445ee0a45d3ccd7774a54ce036ffb5177730971fc814a28953fad3b536c531909042ffa1677de8fafac8eb7db383dd9a88ea4527f70de0966b2d3bac048cc489c0c0c691913e79bc4f3ba6ac385d012978628ad122098600e73b68eddfffbafb4022683ceb1c5d689808d8f5a5fdc8ffc4730eda3426a7caf15d866e5284890ce098709e86666e178cfd0689f4d1a747666fb7e7ac084cd90565505ddf40fc09aa3e8bfe26c2db1432e60f0eb404aeea8e4249a6960a303c3fe143cb55e45564fe5b0913bf8f6c982c4c1f7649985707ee9fdbda1712afd04b600ab380d70905cee858094ca77f0b8a595709a670dd57b8faef6455179804b0479438dceab774cb4d960609046dfc54837771e26f656d613a1fb1831c84c7318ef1b588cdf7d909e123c20359af82e3d0a9bb9d1d26554b009804462a6934cef992bf0765f824d9986f3aeebed385f8731ead30635a6ce040c50618089e2e038bc669e32c3edcd131748319cae641a697f5566484a5b5504973d80b1b3f4cc0d1cf90e917b6ad033ec67e2604689987b38edff98d9ce4bf410f07f320b79c6e4192eec6ea444dc7a96af93823e5f114a0a21b343b8b6ff1a5f767ef1e0b9c2c1e64d85aa608eccc55e23d1c485f9a020aef106f2d718294e71f74ce1e49aab051f13f1142f9082466304d55db810f59e935482b661e03bf7d4200a9033ccb47c4871c61b9a42198ac6093d8d541d270d61e03a20ecad6e817cfc0ef30636283d0dcf39193aefe762da76c6e097f0a739c646ae0aea623666a3c85850c4f1220292cd457719f4071a133247a33eca153bb23a86742cd45e9f662cfc70b8dd504c56ce14ac9281add81dc778f47d84a27cc8d9560089a06c0190f50ef0bb97e18ce7eee5a4b05824fc996d8be0be1a8607a2dc26b9081af9f4c7cdc080ad4f1362d634c656a60a00a633af090d6e88ff387f306dd112205a300be4b5e175a0f41ee2865da3d79bd45526801e5032bd937f0f233f1ccebce99a43140ef234c7aefdd9c4a8f2644a9737d9ffcc545c288e8532d239c5176e230d3b93bf805c0ef1dcfffa75d1d2239524bd3a31b750b503a1e80c93d53457ab6aead7905297ec80f9ba9766b86db4d469ebb8e722662bcb84f4fb56e4030d482e9eff9d425dbade57b6986f552847e2575ae031911abf1a32e564a596ed576d3485de6401df28c94d9f4881155e58c6619602c2a468e27ba9b3df8c12199bfb949cbba410075704eba55021d5b95af43bb0cf783f84504a364fb940de65bff7eb7af405023a786e30bfd564da19f1155cc39b2d3f2a6af67b3b5204687917587c932b06a1deb041c5f0aab75a685874b5e6562c25c68e73155641b3e66e5567a553a04ef0ce570707daaa4b832abde636ff8a551512fd4c098fae9788026a28929ae4f3d09fc6fc052215b7b72f281ad73b3d4ba061286a713532fc71c54d264dd88c40a139598384a5b5090ef33fe0136e29eaa89390f81f3f46c2e61bee9b3fe89ad0025fbd0659972ec4213a2c76d87aadae18718d14abae030c5604ca57dcba8743b00bc5ef116254de78a4bbd1cbe1c0a07065296e671bcb8799caf900f24ea440f1ace537f57b619f0e5af6ab81ab1f229b22e47a9f9c6f8ddb4af9672e142cc4a172c915ae763d93adc9d47aacf9ecb9564e662c30741ec9b5c53ba3683a99ad800cb93318c412ecd8cc68042f1b2bbf47a3057d98ccffa1119780ed2975d2b031a9e72eb5031d48e4f38ab89ec302f2680e5bf6cdecdb20d9b9b3045f14c414a04ce171fd95093b88239b7b7d2705e385c0f598c8d188fb51f46f9f7405c358a2721a2fe172c8f37a88d02c0480694867d40e734f04883503f7acfff6542084c17e4a9834dbbb1f0a1d06ebb8edcb614e50f4d9d69762f94874d04d6fde4c0752d806a1a856674ce00b07a6f0db2f0406821ffe52ee7bb9eef56faf3b678a70b0056caf14c48cef88874a5d02c859c85256e1015202caec5998878c9acc0551d06eb603def682de60f7f0a6b42ee075b80ba4fae308126cd782d7907534fe71912dfd5fff73789282bdabdc9299d7baf436ca341104bc53cf83e797aa7f785101e9d723ebc25c4cff66b835fbe4041916e7465104f9b98478565eb0bfc2997fc0567e4fd5c256c85f68fac3ee24e9319f14d2405076e2a72bc71fc5f068842a71dceac206bd01bc8e72cb790af500bf02e65223546cf33ca170765f805f944070a46b061b5d11b40ace050d8cc5feb16f7c4c9b438eac11ae5981a0618f1d45c12caba43cc1af67eee753b03836dd02e4086eeebc7383e62ff80393178cddfa30efd36409f3cce15a1f3ac25691bdcb29bee5f05aadae6ad7338c675b2c4f96506539f855e4208731d60c46aeb34602b935a8f721310d9bebb0d1bc55a48947829e0aff2871271b07d65be03044bccd3983cc2991e66e1d4fb79f4a45307913326330c5496174b12a1904fc8504adbf956d3d00caf4d042a1c23d426ddfe81de04110ed4e9d82d413131da701c94510acc7894d2c5783821fb1b2947cf9b9fc815c703538175f3c0cf2faad70dda3af3dc6967bc4162e5dd3f398477700f110e24187e1ba95407833ed314bb24f38410df33bcab59155fb173c172220aedb46105fbbe750bfe20184c07aaf6ab485047c7d2fcf738e5519feac385e7d74e472e22b580993c10199c9a264fb71fd89e7cd56eefbbda1cdd63962d7d30107481532c63d7f82930de9f0e3e2c7affb5392975b7d7744702e68b1766ae7de38f60352ca38e67916736b2da3e6433726cd27b7f17e76769b44a03fe3c4e2cad5379c529ab3694223c72b67c02c60057aff7003b3e53d1ec59364a6fc561c7b4b184db2bdd79fdc9704edf0916ff4740514c1987e2f2168cc54b9dd340d02e259f99ce01ad9d5d6944dd52a978e6ba9bca1d277dd21ef3f6928cc2297ee6e76ad3256a15f1009a8956ccd68a338c2e099e5afe9b3365e5818198cb107a9f70be8add7a0ba76731923e8dc81ec699a0ef0fee7451bdf6fdd54f16e3c37bbeb1b23ab07d11a887b6a1318ce8ca89bdd326a988dd00d3b4ae59c1bbbe540469b4b3794666060925b7acc6dab414837c03d8ee123e09f87cf8a12f05c85f3ad1821edf8d12187339266d5d2a9bfa63eea5604415e1d7dfcb7cf9a3df030cdd014c5c615e340cbca315a89dd69f61b352c60c2ac8329eb51b4cacf86f3c467c0199eb1199f7093628fe7f809ceb200f8b262781c164f9e96ddb9ad897fcd1822bee0bc8df832c92537cf2cdfbc0e4d83553deb8249c5eb8bc198f1ad13f00f38e855883a4812c8692f4d277a5639d047410ad23a154c3f18b76e4b062a2f82eaa89035615db2c1f23e3ec233ad71196965522f67120d986e10b52475585d7398575cbc8a81626024b8181e049e4889d5939fea09166d9fe502e50f8e75b8a39cfeee63386890495f31496f1b60165d7afba12d993e5f3dfd22b89a1af0fb254d1151f1b263b2cc367575e069b0d0833cedd493be67d216fdec70fd7e4ea0311ffd0c622838c02297e7a5899d85528b3b21b8128abb3ee080f203fa7ef135170f4eaffc9e1fd0ba8712161528fee2c370ac0f78a6325a1f64768309a307def9f9192feabee851b6ec658ab933729028cc58961923dde03438a74bdd19ae209d65dd2a2d1b2f801c07e1b41644974aa3a9de8bb0a0054e1e4e494c2330029e9121930c07e1bf8045674837a5913cf33bd338e0903b9fb9c99a63584d83fdf0ed16c4174334f4a18138d91a0b174724252869878ed251e5dece675925eec92cdcb8215e5e4962d21989b001f106ea3d1a5143133cace9939d4bfd6b263bb3d9ff7853042f7766118940257dcbb06910a1c5809866a3b2d5d12fcce6ca65870c1f9173ff5c9603622ff578cdeae9d93d70ef9f3be40e103ba76d08fa6093dafe5c1b5590a63b9e6304326fae968c3e34f8880064566f2a96ffbe50721f2b5da52d43a5188cd710b1aef98bc4b71623fbf42b824b15a39044f7c1875e2be037b0918772b3a4b433e22a71b722d0878620af6721d59a1053c50234f4f03ca953a37a156fb6170f0190ef5bcc4e73421f17752d6857e24e8a6adb26f80c34d96bb8e613ce3918d4ddf2ab74e92651bf9c4c7be3eb2ab58693be33a8c3bfa0d51d077d9e2feb526241c058d33d0d04a34025d6513192d57dda67ec1800bca63872ed03c866eb1942db6256ab2fc551659b6a6ac38c0d3a218537151ac2497e6ac5d1a6dce982811fd2c2f72c2f6ecbc03420498f9d2d19c28416b931d31eca2a50bd70286778b5d5b3e07a9fcd192fc5c1ef2b71ae66a29d9b86c423a24c3f3d6fba8e8b6fd3a3ef2e621be45cbe7c8a823068cb6169ed79d25885594c68bb5707a95543a8dbc98c83d2593ebf226e6fa831bff91f121397319f586173e4fccfc25eace77d338669586066c1d07f923cda6df3e64acad98fa0c9bd48f3348d5891d153d215f0ef0935e04b3d439f7ae1db0ba9c09160acf0c2dd57dae351e81a229f10550f78569c69d10e0555e194d2feac7c390651f902cdc61a031e3cec9d194ae5a1abd14ad9fb3155a16e4c965278c08c26a4c65b9a7999685e602dae2f686e6eedae39af4cf3c1991c34e32bf7e092b14238f99eced1da6cbb658e72f399c5e2204cc4517284c2f2940346864a12b66ed991e5d9e54daa380c27ff63265545bf809f1cf6040c62d1dab6c55cbfbeaf1cba651c23b6e4183365896bb6a75426fecb5d7e8588625249f65de21a1fca4895de7b13d96debad13bd97edfce751359d75e90dd0ec69b1086a85f55e71d8c65942a39cd57b3ddfc66d17720f40935dfa4041ac91b02ee2e0b88602eb808552a94e2596819a2bd322aeaf970f0b9f25520c979b5ce334e1e4c41062005db6efa42f26b3afe995f3c612a3207a60cf0f86d9afb2f5c3c9e2b98ddb4cdeb681d7a79a2a9e39ee354e55db4f9489931d684aa43609c8c51b02232d661cde0880715c6d7b0d9bf48da01e99f43d720ee4b6d06cf67d1ea57472ad7287de202f92536196e5e4e7efdc289f2e8d29dae80c0ddd34e43b06b700010c6d2dd5eb40b30f5c1f34b1cbf5dd29a9c6413b404cf7f4d5e44c6efd06ebd2f61b00c6a9a23c2ee4a912a346bdeb353a24bb54066c2b48f603ce10bace6fb2b6b2f67b01b1cdf7418a0aedea179e0420b190977ad849bbf763ea19ae22a641e5416a795908c6a5bbf2fc3376ce7c9a189e4852d0a59b1b31cf12bbc26a43c0540859ec14f887cd35eade20a86905e21608136a7b9225a5aa16a9a55318071120ea66796e1e296f0eb89c9dd8a77ec55ba003d439189622adea8684f9c0d7806f7f6a167a329588ef99908d323e4038d7888fb2fd558cca93ff8a8e360148e0c47df63964e87a2f9e5dc456e9371cf436f04ad4ba531b2560a9160fcf8ffe21c8a6392a254c34a14b1fa07415cb779b4a97bfebf9c856716c497c04ee020dc2ba8db6e64064f6a12864b950b4fb8943d07c192c6b3c64636d04e920242f0b821e1e5e9468a4099bb85d0dd20a2ca0ade28850e0c75a29e17d7ea104b967dbf1a0fa7c3ca7ee2fab640e3993c5034cc9db10e99422ec8393af23fc1244823540c04d3650598e70a47652d82822dbeb095bb5422a85ebe8432d25a4b6c4bf1ff0f6522a9ab211ff1043c79af9f816db1a3ca129f5ef5b11c482f7db11867ed3405f151fedc4ecfc5d689a334802fb029a00bd9764908c84397b8ddc832d2a6ec0489befbd5fed24727e2449e82ac9a1405d9a21cd2f57451b548030bd8b00cb41b999bc3ddb385c86dee4bf26a1c90865a9cc3fb683378cb26248fa87424483120ae64deebee903fba7708211f871846015acd4d576f03d8b04662244b5a64260fded3e599ab32a66c2ed158174d3c791cf146feef2486d3458fdeb49f2d2acb27580f9291d1eff894607e2883f2efd696c8d34ac9bfd4a586b8fe056fa28f6e10aa24e33810e8b61312f869c995d4aac65dde2d094795668d54b0dc219fbc062128339cfb1a949f000a02d46388c56437439dbf3d755e85880f52510c59882120bc9892c6ca86d070eea1abe8c2566f701490a9c58012eaf3b6d6d126927348280e5413075437951f5f113bef1d4df52f41f45df426552b611545d37697ff1f0292dafb57a127f033d599a0e08071988f73608d79181ea1a4bbae27414bcdf315cebeeca215eb368de29fcfb40816567217203a1d1597dfdcd00b5220a802430a9ad88662352c218a6dcb7b7e081ec4fbd5bbd0bc983b48d405bd40db07970719874b5f0f23d5cc8242a11f54eb44441ad3afe5b59faf20a86bdc466d2af63113b7f9ca89a8532daef2d3d094372129a5beb0fa710ad73f7aff437585bfa2f2153d127117468e302cf735c858a1d9249dd51377d0c49e5c5087cf8c8e859e80187fd2efb29c999cca326d9faa50bee31813c4610afe50f2e78e140fdc05227c02223b0524a911ad80988b7784337087a40dd271d329432802b6eac492e7e57a0193fac4b5b1b03a48592cd4211acfdfa0916177c1ad875e3b7d60bea7f023b72dc0b82e002721ffc03a4e8aaa8bca823e3323373116bc9d3577f80eed7df61521e0ad3fb1dfab09b965cfa000dc910eee2e99a86ec7debedd1bfa6eba05844a1a880f9155c742f51e7b96419d512e48f56ba3c42b5d7dec49c3500b16c2084c025f86494d8ba7df6789e6ac2df2131f4ba25d5d554d75b2cdf1eeee4e44d6e421d8f4dfe37685b8da071a2112f4d690cff5e495994ac9c2af79f6d1244f984427f1e6c51dfb2011f9d1154ba3d84f2b23b52897c7f2f4a10820195af3d0be462564326ea6a7a6d7e9b4131408df2c8207e26410fbda6e2de4f8977c53fc0290", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da04570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a7110ecfce55cd472966bf92d95eaa2ca0aa89a06129ca6ea67d7b5c9e26e4925e01de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811088db0dd523e2dda90a02310bc443e2796689bb889f1a91f22c7e910915558a7279cc5790a050ab4e07fa5176b9018996360291d94e5cda28e417eb2fdd3e8df" + "proof_hex": "0x000000000000000000000000000000000000000000000002b30b0403aeee459600000000000000000000000000000000000000000000000db8eebcfc671fdeea00000000000000000000000000000000000000000000000af8f618d2adad1ebe0000000000000000000000000000000000000000000000000002c5040e9c20dc00000000000000000000000000000000000000000000000a2de63e4ca6eb0e210000000000000000000000000000000000000000000000021ae69d4a65c7a8e900000000000000000000000000000000000000000000000a005bc241e7d733b30000000000000000000000000000000000000000000000000002aa8c45dd85d4000000000000000000000000000000000000000000000008ff1dbd0f4b9b41c900000000000000000000000000000000000000000000000a46e1fc8b19f097560000000000000000000000000000000000000000000000075006eb06b9214632000000000000000000000000000000000000000000000000000093433350ca3600000000000000000000000000000000000000000000000c8e6e0c4281f705a50000000000000000000000000000000000000000000000043c924cc53c6b0e6b000000000000000000000000000000000000000000000009a11c5c7358610a3300000000000000000000000000000000000000000000000000001c36379475c900f5f028b1ca9b05f4fd64f348afe95c048291c2608303e91a5c888446ce265e275b7caf50609f9aed4ce3fdda4988dff1b9cfa755d22a06ac9bd70fe011311d3021f96a67be4ae494bd7f310637090a697383e48e1cfe83d199fea464581f5b26f5d27b00505d5850189be9bf43d8b14ee58b28a16c87b3bf9d5bd3dae341de20a977a63187eb07fb817d6f32bd7e353ea5f2e4563e05af30e41de8d76caf2a05fb7411e0736765ea872b88f93642e1a0a122c2beda192429812e49797d0d2b0b2d57c7f9a3d89feb36de6e8925068d4cf01c5631bba6ad66d9dfea89eef42507b82d0cdde68023f51cd886423293963e553af03761d6bdd7e82c5f3f70581726faac8f74102eb6129ddf5940c15a1f473a25099ce9aed7c8502f304535e73f02c89d7edbdc59430a931b60bf65dba6ff6c4568fcf3361abd1e9097cc3446930d256472545bebbdf0cd8202744ebee6b32aded93bbab7c8b07493ffe15d753c2e767d9b71f63f422e0d8397639315c74a3f459246674a141133d6068e91cd04236270fbc4be415c1b3fb0340ab79c0aebf168d36a35a77d26f5507817a381e82997d3d4e6bf47cf282de75db04e846b05a1171e1b27b3378df7f46c8a0d5f452ef0980db9a94784dc6878f8768320b7a5f1327d5a0398b9090cde4a0301320d2c66315a1209ffec932d6318d851ff138899b2fc2f20210b9ba6487e63272dc9292a55757e58a10eaf53bb32db4bd956cb6ea7db2f7d3d376ffde54a0c481c8e2b91e9e7d106a81edff57b8a49e69ba1cbce8d8ad7a768f51040cd0760adda1f1b057d97309b56258908e7d0b72e311d563d0f33a89b025e9b29dbae8430af6c1d5dc9be373e533150316757b50b3d7f39cb3fb983eec9d368f3997f12567c3516a615929225c27f1166fe0b8a910ade60dec004d39048fbb5e348d1d7b8f14e167cf8625eabdff7f7262e220e7cad7c3169f11ed47467b15790ad046ae3ea490d5aef76bcb164504f3b903fdde68d660bf30a4a023c0639d3b0df5986a898291ca9055a779c358e4526204e571e880645cb2634ddb604f97aab892dd8b2c61206a8ad742b79fdd1cf600bb1b100e239a25d525acfe6fb97a5c0a13dccd21c3e1be9395c5f4a8ce11c7c9adf0450a3dc13c753c8b0d37894fdb03746c10c45e92b11c11485fd5cca6eef8495c4fe4a8e89af5ce2321dacfb3de59466cdec2510156f196cfcdf53325e7070710c87d4451690eff29e4d32a8e34518657116d7ac1562534b4b1a9267d5813b831d136f643f2511cdf969567bb8528b3b509274e022485fa86f947eb6caddb7d6aa964114d188ffd141b79fde4bec77997e8de82e1a22e50c704d7e5fa81ad6470e6e610932071304bd9547f446e615d36f44ab582ea1f51c35052fc32ef3fd4004dc82b1bf9a39b670cbd3064b42858de6d62c0e202f260e6c01dc5da58f0f5ca5aadf80c9a9810e700b5342d312a883895208ed277759e4d19186345a6d59fff1e1b2df1b9ed20c49e08dba5d430c5c889d232c0dbec12765d1f90c1243fec4c3d67e1d37e80f7783ef9a1828baaceb6f59bad6211ce93b78dc48f06df07f27a2150b2b744cf2a5a06a0df954aaeec4161a424e2eb3a35a6b9f4e985ee41ca8ff60b38bfce07c646fcb9cd13c438c57afced26e28939f478f3c70bb968c92a82a5be84d9453b1121028cc087d1d072e7e6c7c0205fe83b1a01ceba1e0e90bcce21bc3dd3be897d503f20f49b07d818120c8883900aac83216fa6bf485c26476d03e6cad5643b6c5cf217f866e372b1ad25028ac239027d059b6b09551123523e2a5f42d33c4b330bf9b77e09785e6e48fa657361dc82de67104ad93500c9741939e8ba447f6dc1fc4191770b950772b9f1c82772b810282ebc72e0e003f822c1a213b3ce88f42565589de9c8a22cd5e3fbeda3c210803bc3b27db480707058c2df76c782e29c4ec3ed4258affc6790d85b95c0102af61b154df03951ef522e8c2e2d7c0e65d89bbbac33c8119dd9bf436def3cc2b3767985ed2bbab59f583232694b3a362918f9f72a99af2f0f5af2efcc7697c2d2fcaf7859f1a53585929269d86b3c8973039adcfbbc308fee491644c480a7c27dfa9fa5ef3bd09bffee4577c9bbc3690003ea1e3aad93e92fb3d3049d533190deaec9660872e7fcabfcddd95bc009081df6bff849124e9e9df109416fc751b0d2a98b66c8baa3500d82452f976c890f25ae6399064f155ecdb915f4d008b7a002f0bba3996e8fb749742a4b69120e029e62ad24d4f998cb586da076c916cb321f43d09bf02b75a56631867d73c77cdf9da4de3492a4b650d78b39b367dfa510fe1e9c7137dda94b17f265f6938deee104649c6163212b3f9be3a66234369c113a8e3a54aa3505c8bd668b9bb77dd5eec4e994e0024de8f3203d92f6f301e1223b57e5d4a69b68f6d9c57d3fd2dba27ca0bfb145ab0c29922b9a2b129f0e3f71fc82a5b8a4a881948245a642bc118320f72a2723d6a7a9ebb1bfe185529acce021f5fa2445c3744c8ac48355df7a41a8520486e45fce375c3c6fbd3b33bac3c2e5e29bc45ca7fd84f34893fc756f888c9db6e8d1b33d731af38f622c11c5e180602f9f504ce236d5dd7c1068c9f472f64905dde988f8e4ae7f199def886026f21843689e58f2f3dfe48f8f5a637b4a71284fc5706e649278419b7c8e538926409cfcd311cf3c556de1e275dbabc9e67658d311ba3cb5c17542c186a7da2ffd129684a2f9bdc4f2ebead34aa0cc6f94cbab3aabd66c8fd8bf65350be1ef08abc28a673680e3f8c7e41567d1ded0d1c173be5f3f0c7e080b2f1545630ef9644321ed718522ab1f7584f6a4ad83079823dbe5c55b19701cdf61208e783f6516be01412b7a8746a2c86c2cacdb91aa020b8d1db64dbc78c4889165e11b4aaf173202bc28bc70e00bc24bb8cbe3839f97793b66099528624e7d1c7fca00b911b707b19bc81d8401fa754ba2a05bc955437d15102dd440d1969450617d725d1c0baf51b3d8ba9c5943f28453d574f21fc2f9a08f9752aa61f9188a9cd8574593a82ac1794392b927399e2682bd77ca428316d1a2a77fd52d12be4d3f7fd57b9b33a68065b4112e464cbb09ae6d219c9acc029a295cd67fe60517b04596e84de3cbd1602082cab3609a950d6731f100664632ada637279bec7ead801d854a2b92a322b0f0f6f0554856fed457ba9fc25583f8f1ff8d03d55205a052c67db1a02282cc80559294b448cbc117fe65a32ae25d61584c1993159d4c2329f89293162f89ad02d91f842933045e44515de74b0f6da4625efb734d885a51865862a4c2d8104682c6d83cb7999c411b070ea3a5bc939cd54c26d43025654b66025c683116bdf06294b2eb9482a612e3d67a632d391609406b60edbc28b9c01c5d5b43796bcc3d007d36fb02537fcd6d3baa7bc9c3859846fce0d7b30d65860a5d8d27f6d2718ed15850e16cdfce67e1dc1d63b0fc45f1a17a0d9c7db71f689e3766e12786b8c2a2d60dc28eeadd07ade9fbf8447fdddc4f91d05b10d136058ad481f9b4a7e024a17029bad7d3a24366de116e8077da5b101c14da26867735d2524bcb9864778e02dd5dc33744ca7e50e87806b9d773d44dd60cac69135a5d29dcb4b93fe58551024ca2bae5c4c5f6682cb487546e40abe929304b714f50ea72c453cd38dd5e8692c2ae119ad1d9ef3b605e627d35720d26cde98e914c811cc457ae3cd5252b79e25893e23371561e89a731da3343d9ceeafb13593ccbf355ea34eba49f2e63c47289f58bd3e1fdacbb319183e85582f405592fe19656cd0a23a1cfe79d906f80d1efe414b0a22a209c332036c65cc9d46751c183d8618b839afa04eb21499cde40a98b16c72709e82be7312a6085b0563e940fc80e1cc10ad90615a040be766a204743f7867ec779c8a1d0d5fe3de9462f19bfdb3da14dad8768e04f13148df882eec36da9754f273e15636d808ea116523f0d567b5316bd1ebcf6c4de936d03908774b8f1d132154642b87c20ac9cd2de8088f1d0a55de6db45a75543d1adb2806b5975df484e4db990be106045d32642afbd7a492681e8681c84ec41bcadc6e1527f9ce6a7a670a20f260ae45d21aaaa489855dd747e5340f01a956152504a31ebc51009014c5b28e9e845c3a143d89302c81d5f94bd499250d48ddaf47fcec0afa7a61480263c5273b8acc667e784402be671586cfdb7b4db3f8a0f5e1fecd2aefbcf44e5f04a3617598869909c0aceaf0951635951ed4180a0a9b50cca1bb2b5b775f56a5b46a804330edd52fcbdf477485ad2f90e1aa0e3c6febb4f769eb0a4e62b793e1ac9676a35745509b1b9137cadb86a1fe28b0a00b1ede0523ef0229d5521820274634a635d398df9b85908f4ffd8533407e995995fb6a6f4f8bcf15aeacc9028dd5d83f265d2fe95d44afb3baa593f702b1937c3a27a40b6f02d7058a16f98da0cb85c8c6cc07d602960eb28aed5a6b5a8005363783c4c6ddb2010fd07d26daf701ec005d7765e9701cf8617925e4dc28744355b61d1fc2dffd272b74a06f3520227be984cbf13925584c110fd52798ddecd811ade62f107d939c1379e3ebcb8d71f09dfa48f3f2be201f2cefcc38345ae3ced3286cd06dec45e9115b345f3e78a3ee45db7f9170064864d5c09cb0fc3e8f5b7a763846c9b6a87d11798becb574a394dc37646282a7f7c7db3824cda09f9f8a5741937915979ac90b4f7b5de4d1eb4a3b7d6181a242ce8efe9e99513e83b53a66d91e85ac3412a40f2ed30dc99975a2a8aef2f11dcbb15473e41cbbc7bbb7adcfb89f3a9aa708170af029423d5e3e33e5bcd5bbc040ef8843868fc9243d05539ef570dbe1e10aef20fd382c33955f4bb9721ff64b91dc1ccb64d1c39f422bad3029fd747441ead524ce950b1a581caa7944b372cb78c0f9933a173143d3c99f2ef5203bbd71341e1b580d87c30fdf3e28038e52e088ee5d1729da0d34f16b6485002e49708b2af80a0440fec31f89fc13213af1d473835795cc991ac856248d6947c032bea69585093d3901d6393263c2e887bb58cf5f9e8dc4da7a20bca245b87607de9ea0620f096ccbcd54b6f3921fca613160e8e406d7331807a16d33650e418f91a75a1b49155d96ae9ab1de581aebaebcb2cbda8934bab4be6568e7c921e6ee1e9ce8ac5b280bf12e8b4976159f25c05e85fd4f48faa1e5042455da26784f0378846b00b1019ece0c5443a7276dce936601960c620c0f48dfdce3d7158691ade1f4c89e8e16e33e4c84777d3b261850f67e4cafd59caabfaea137826ce0b785a1efe2a1e308bf9cb95df87413d1bed427d514f8d2f1ed5215cc8cc01b960d2fe3466acb780ec253f4c5b3916132fce27c7d53c269e49e0d5f5cfc302c1815c83a39a842a4196b71ab05ffccdc4453eb6121cad50beebc9506f5e6e7a86325c089c1505a5d1565e4e200c0f947704862ea0bbb30d55ef3b0138243795391ea5660caa47102215bb22876b29def7ec75957f10b4173c7f04355665519b869e5d51c8f7e156b05af22ec3dffc3be18ba8bef4b6112006d557dbc866a984f59a6b4850790c67d09dbdd531ecb6baede53b5f37ec5bf114667e1d26ff037959d61dda4206f612f01df169922373268b1e7e6fad457ceafaf8c9c25e62c37454897d9732b6135712bee728c4eebc919211002d9c6f8735af7b8c0a544391a46f28c3a889d0f2e7a104554aca10104e8de7cff3649c20d0b1f54088096a7532b5019e476647fa05e2080741d0dbe713cbcaaa2afa2303255df9d8e0d391a6532b96bc449d704437222d0edd1fc730ed424ad8d4b5a39369538c7d586ba3e59184dd5f2902ed1146e1a01c7b01250ab358965ae82a0f4528fa5ada19814df0d73e4256eb85c0782e527f1f4d0221ad31b598531d3812ddcc1499f25e1501865e17df02e1b8cbc9909234c2802c41b8f3e59bdcaca7bca354ac8a06eaceca80e576276e089cb5f29e61b0d1e6d85b56c11df39d49c0836c0a3db33fd564f9b81b2dab176127f2020091e84be762866d62955eb34adfb2c036dfdc79652bd5168e1eccebbc106c6e0842a6ad0ccc37f360dc838e8fd4104c57d8d74b677ebea4c070a1d5e7a7fc219ad1169ddb19c6d2d9d077f9a31b480259d7c4dfe8e603ec1953634fc56a67550bf2f4c127151b656bd10e7db9c9b4e19e526d93e483d2beb6d6f95d5dd7dc4ef270edb572eadb4f1e9abd0639860e241ae44ee499db89e92a7971670be22941efb0fffb8af56e85dcb0a48c09ae10131bf2a64b0ecbebfc772259c580506db69ac2156fe9c7240f7f66d4714e18bb20c07f44dc0cd84f0d9b1aa509b66ea5e255e0648ed0a2962ede02665952151a54e8cea60f1ce897f00a3abdcd6fae82e74b6212fb5e7fae591d70df03de924b74918ada838ac568031697a44c273484fdd102cdfa567eeb6b89ef0d9fa657e8943950256b1e7053b0824e4bef3666272ac0314f463083e7bb01bf0a666e722b81cb549429070f0cb17f6cdbd435b5a9beb8a2e4d8baeeb9fc7abcf248cd7dc6a01075c51db5fa579aeeab43a582b47399ced1f5044f92a873e6418ba412cfa0a8a2db3314585b220ed790f35ea358f93a7630fa379be8dfc67d5eb98c3aab5c2f533d6750b19ea957acc6bf9e2796920007f14afd5d25310bf4bd31ffb348452c2d021e55f237a212ae30aee3f52d514f4520a9af18e31091497248753927f99eee28859559b268451ff74aba9de02ee6b4322d7570ff915b07f2cb9c60a3b39cd3b93d33e3e05f149db531e7da3325e7bbc1384880b0aae07a0d00123c89a8dee209bd57008f71bd1a6a5778e3dc257782b1f4eb8695e6c34c7d409d48f8dd796db2afc7267b6c14299be5aa8fc680abb4d14671db1342e867fff13ead25dfd0ec4deae57cb9c3dd91d62eb03725d734f841d9749ba103d39a29deea0e29fae6d44193eb8daeb4b06ceb487292619b51b15221ac506544d7bbbb235a8b1e025e6f95a55d4a9da6e2269cd0fb34f9990ebdb13cb608f09dfd348a611efeb3a20ba7d4911414ef238b948281e71b4aee677131b1bd90b800ded9e14d190e183fc69564ae4f8e218035a2ae98adc09e2dd8911283d549ab1fd630223ac9b69f13ebd7edc52739fa0d86ae7bc7edfa3923fb49f01b6905210ff375d48be2446ec8ad19fd201030784ae30ecda07d819104ee91e060649b3d0c9e9e2b984ff05a62d46c6e9bbdff96c343c82a6512f86de130e771a1a878e6a41a6ac0fc0788e6b068a9acdf76a9e550c760f1a7d2054f006a5c71ac65c82a640ecfdbd3853275aa0ad3206b715cc3ba60919d7a2d307a318d84729e3e17fc47339081b3733d471aa0c4a156a7b54b1b9285d483229a32bc6340e223d87b4069b04d0fbbc22d2a54ac37af4289e9138d2648099c28729bf009c2d28faf64c8a8042fffd8bb899554655cd463010c1dabe9bd2d7310594d704f1e10ece778e01fa0d14bc72a9095f381f288abef48d73e1c8f70323019bd3790cf90609f16cf84220713056f45f8783d40e5baad22fef47a7e37b46ff86b16a7050120dd8a0182fefb468bc5f043da2c84b3c3c1233743dc0d3f812011ae063c60c20907d674016e758411738d08cb7b38ec5cf2675460d6e4979a92f1a6b69a6d80f09ae9f8647a6057406542e4b5cad4d751ce4851fe2043791ccf35977c9694b27ad7a862ed2c107a8ccf062000dc496547dc6b4d6f3b0ed91a689dad868bb70009cf51ecd1b12f4f015bf9b58c6a3473e82736e632d1274d46e8fb9aadc92ab01e0c7c4c9d3ffe24d2a3ea98e88d74bca4b8fc56d7d94fab738c555f2cb4ab724de351101cb87df73b68d48f182811d7bd724087eb2d3bf773a92ae7fb3023d05f7d92e780bc1b98e1bcfb57bbed3078e50104684c04a0297a1f054585de92e285d2c460dd30427ebc79eb4e26c0479727e1884df19ef51e10dc42db993de31190d3d6c988c1c3dfa4e0105fddb7ac35563ecc72a4a196e035f91148ec48a4b119a13ca4a98e0f7a165f6547ee9750bef23cebfb0e79c139d02c6565e92464618471f2483429f5093edaca8b65ec2d966a66317e4fdfa8efb3df367f2511fd514737a1a937a481e7d8cc9a03c3a60841a4bf7f9754bf2fbbe5cb6164ab948c00b2e306a251f23b43d7d25e038afd9bb94e90efa334bea6c0c33b9e527b154f0271d9823269f3414e5b7274cc402fcf960867cc389df326a885484a21a1730d30e0d40999fc462c52b6a00344eb43a6e589008ec5dabc7d3b7eadc04cef996180edb9f5dd33c7c3ec5e30aa494b252658237e1f49445b383aa5221481bf279c824a408a0cba2b07ae7260ac3a8b16e1d481d8bf795611beb58cf4994317e4a8a1f43bc309f0b9a6938bec0f485cd9f959630aa5929c9a94e40f0fa18f3ef52d225323ffaecbab738f0a11859f83e7823fab2368e875eb74f7efdccd9d03906d70bc078f6741689487c98d8823c61dfe17c86e49cff25367cce5f92cbc602a350181120e505a5904a146484af259d3cc7407766ef0f9d0ed2e8c8eefb1455df430b15d7a20b2b8ce47711235c340fbc0f7cd120ab132ce965274b695e8384b0af2f74f1a79885f6871a7fa28a0bc7c26363be0e27b50ec7c8fb14c67e80e2b47118205e0e8472e0dbd61b66277d3439fac17951af5ca2d6b5af0d524ccba21b6b2a6051dd94a1b8d7e7704680ddef09a1b1c4419f2a9ec9820da589e9b20a70ab24d89962987ddadaf53f6605cd3710fa08fd60f3daab58c4ebee1ff8f0c7b88616302942ef09f82722e003f1421f3722d0df287cad3f920171162ba91a98d6032d74219ce8c4fe3c7e33b0ffa14c83434d58870d116e6c36559a0c5276bd3ede2f133f4b9b09ef25e76cc4ec02d3a62df652cd9034f372c30b66891e912e1bef1e17181b25d1ba2eacf2827897b7b2d0c5995ca072785082d059c8644febf9771752e9b58e4c98cfc60c257c9111c8b2bf6734c7ac89d2bf8276d6eb14a2be82084e6fdf8ee543c1a3922df2d173bb2d7a7124e55a2d39ea70742821f08726c31b6c80e32786142546b5c35a2a9a2441745ce704f0e55134024b64036bd28f8d2b8119e8dd0e5cb9245d8c9058a66731ae81cdac1c54debe97ba687b26ff00671d6b69bb7f1dd005c20971d3f86f8949ae90483fc8972830abc6481f17f90547263749df4c5def86d1923157f67fa1738bef66ca4fa004e6fc5b4af721837799245cf824a7f53cd08d243f550311fd4a3d959daac4cfc85f32604bb35af71ca004ed6051ffde6dc1b4fe2eba6d82661319e9cd3b855ae418d73cbb37864fe3c10c42f2fc3567df4971fe7a1774e33b8a72c9ff4a7c9c666df10ab3fc86bdd66d2337af6fe5478206b02c5f1709d87b9df0b8fae401bfcd98b33c30e01e93675f1f355765c7361b84de13e9aef9e4fe7aad73a2117c05f9714a0d361e693470c80506dc72a579afc28392a084af4bb406e51d53782e6ca9ced5f2733ad62194041187cf6819f495e3f36941d42ff64e68b479b4c98047b50d14e9d906b8970b9a1a85df2ebcd9f91bd9f9e23d6a103a4398801cf9fd35ffe19b1d22551a09c77e050ac86b551234e153b9b1f200e5e4d128ada63380f68aaa80df274d0bd10de6129ec811001ddfa3cda1b108abf1a10645def9a8f6089074ff283c624782d8e507e2120f8237004abe881653eb8da68e34bd3aeb10e4a19a8fe7684dcd8fb2160e98913519c62998314bbad28005c4a83290a62f9334bd58031fd66bb0db262019b4cd0c0423184d42dd0524ddca8871512144dcffcd066ff6ebf9298ea84e812ae69c3c6009d6328449e1f9eeea8fecb6b9e5c96a196891777b729dbcd009b11dd6c86933cad747984c5842202dacdb14319ca09f16950b4ffd8e8380fcfa9223d511d82ba615fd3506e86326f1cda9dcc1cfd9e6d5f1ecefe79eec809734300c4b915bd8b2f50d9ed1228836ea07a31376c0e1f4bfd053de47bc04acebc1fe0930c6a3476edb691858be52b6de2b01d9e1e4abfbf3d3c5d2684b4bfb7c657919035941a2aae3ad42a9e63465e499c16d2dfab35f248622e7e58e84bb84b71d01e032044b717cab2d569ebdfe7d5097752cba06245ffc7cad1b18d79eb62ba3118c1524d59d9cefeeac71e447831f83c6f290ca10481e6916ae350b88d60f3e2f0058bdb51489572e95e31fd84ca7260abc39f36938b4f87e6d4835f1ad5fb221a652e8262f018bfa3856a8cbd34144e6976d45a453f3ec06fd6e376dc4cee60c6500c3280e6ab4927048494018c8afd9bc6fbdcecff3018ccf7d03b51a6eae14b7f8520c39787f70776235f01793d7e7f745848413062433200ef7f8cefee602784ed98b2c9d273cdecb02ddfa18a8a661d741f3bf87647e3b8d25eba2360217f3b3108225afa2797868ceaaa866d663881a0008e416c5f2f80af7149f93050203465a7c2fbfb1ae9517fbaca023c9862be40b2c84f30b81d237bd7c3798c30891c8ac7a91dc7b0b69ed9e0982acb177108b867adebdf348d894dd510a0b7e1cd010d9147a550c8f12f60691d111628d9182c83ce9536af7ead503f2d86b1b07c40fabfd7c29d47e4f8720b7806cee007281fecdf927c5a8090fe200db873a0142f5b631901ff537e84ad96b1796bb469b1b11cbe42c2610dd49aa74187a4129a1854db8be04d0079d6f88c31d140eed4d5a1139b6cf42a9b792ae577cfc7a2879bf32ae3070900028e0e30c27d2c5d1e44d731bd3d2e6d6135dfcfb18572d107551dbd61f608dff663d88629c18be0406c8ba65f8ce3422d544efd0e1e9000dcdc683eb0eece6d0ba89d46022e636bc1ca13b2918d04900ec63e29f630e7b09d795652cbd6a6479cde8cdeedcac973f5d240002f8f6df8a75ecb9e1c6d2b609938753bf2c7da02deb6f5f3bccce5e9ca2afebf91feaf1265aabdbe2ad99212954377b6f55c3faa8c03088b93f943eb08faff93d5c958f119889f11fd09ff304933fda02023e503321f3311c3248fc12cfcd7e748e21b1002a39c02c01d09209a0ce90d8026f074000e455d119e776d349ceb3da94cf523de5dbcff0f48b0c1a8048441e7c85fcfbc208ede9faedcd17cc3feecdee4c70aa042c6e441b059a14cf0077b5c8565e3c03b57181839f9f7ec98fb603c272189a6ff3a1d3a4ab621ee17eecc490d3d3fe3b69830871d0f9879d3049ba97804e641d42ee31522ecf1859fd0e2feeff6c4515dec6601e327c6ffebc6958cdf901c0ca743f82214679087d9626e792e9ee6cd489e79bb805d2da252d55a99150fe19bfb245a1fe42170dbbb9e93f584301289b415aa57fa49a31d5a2373d55a75df54190b1e216324d0c11ef5e89f61d21a42d90763ef5a89092a10e1acfb7e523d7f3fe9f8efbeee02c371b165637262eed95245171b22f53990e63e5d7277c2d73a4c391af7f2e4e1c18101c45f8cae2939e17c2a4999e38be4e8683139cdd427060e6f476809b670f35b2c56491d7f825eba12d6626da300fd3c80da328f0ea49b767a41a88ab8321c15d42c0af8fadf6438d42bf60c3a2b3adc5871de99e21556ddba86feeeb79168a27ba4c7f46b716afd29c915ece35e192e47cdff65cea5c35dd963a7b9ba5232bfcd3e8240bf1278b36d6aa6dcf916c22e853761e388d3f1a40a433aa34b506bff60e623daf6dee09ccd69d2781bc4d993565b6370e7684d7aaeeb2b1fca601357cfc35f67331a5714552e9eb3dd6fbf2119bbbca64ea76c836020d19ec9a1f48e6e38d20777c1c4cb8531ba222faec847efc90b465e9c34a7001ae1aae1a1b5e613f47a7d2a35f2fcaeb1539fa3306807755ed92968758d518fb4475f80a0a3ca9d6a6143384cc6083082d311bc387ed1b500266acb41b8126dfd9c93e1b1fbc2e1f174be1d7761604d9964bf196b9ee0a8706f863e1822950769d6662051d0bfd2686444c417b5ceea329e2584442c5bb2f75f8e7c180fedc7a3a6dc21014bdfe5ba28573f6415f77fa9325bba1727fa321aef3c424a762f29b3bf455f51babf7c357a4e5ed8cff49b4eafd2746f73fa06b1d5ed4bd15999b64d6f3a7121d69a9229292044de7007ec9f717fdf09dbf3b367ef4a7a1baa46b10b4e16ea903e4edaa9011039efcee40df9f889a16f1d4f4a9a29abd4c5d587197dc1da1d8130ef6d7f55840a3d60e13734ce26b321d00aaab7088ae1779d826e99d92f54b0b80606aa57ab42c90e751dc5b716baff6a81bf135f4250f00aaa0df2c833761247a34288a2ea47bb0bc836a6fef419c37495bec860fb6529465724011b4e58017dd1415346d52b19101d5eab12b19a0e441575d47307cd7dece7b39f36fa26a012112fcc778b55fbd14f16cdbd3acf9a9169d544f15345cb6173467874f657f11a12cb8d9cd9c39420f84a35791fac6511b78d593d42fc2b8f5d872900954a406331151227f3b4ab61468947e78586055c0c09c0b98362664e5e1c8975eeaf02cbe3fa2404ac419609ca705c7393296997f81a9c8423adfd4de864c57a97922159f4fd88748365f7c0d07563e4d453417656c8833e29d6390b86fe4b794250a151fa247a5e1b9c58149170293ce42a65b9cdd8b38d9ce0ca032a2513a10b9aa13bd582849621d7b26e71960d6ab34544826f40f4772edbf65887cf9c90675a02a24cd768068be5feb2b8020cab1f939df742e78b85890a0ed8735180b8dac5a2cb32325a25e5a55c3dccdd36db1e560cf06d8edaff94c8f8793df454115ab6907e1433a2fb8a9710e410cb4ae29b749c5a56812e5d7f5071e3f281e22439f66040be430da6e3d00d79a926e6d938d9e102c8de762f46132cb877c9cd0719ef3124a78afd322abf925c981c80564d0990d6a8ce664e2e17c87b4a0f4d853162214634684c7c01135a65a96a2dd46dce177320faba9c4559efd833f3dd5086ba8112b30bbf4ac3c13f437f5c875730809567be74cf8a9c816f4fd6716a9b82a1c0305ed2a51e4878eac2537a240468d0bca71bb3db3dc44587370c898536d53d80368b46b639edce91842b66e208b95b7592b85d9d72657432787fe6037eb85211c1c8adab3ff2f751ea8fbe3a00085f306569ec17b74af594427ec4560689fb70b4e6ed89f6eb53a9e4512918249b9551e0ae6c9f36ea559e9e512e2689d090a17415450b584e5c7cca50b33fac3b1787034d0208acdad636fc3d616d98f1dda10eb9991d957f037ea435e3319c366ef658c47fba775755c948610e7a384fdb026d34b79b7c65045b283b693ec39a3a461580d5e888e2bac27ea2c619e06650b23ef7817b83eebcdf4c1046def9755d8aae19209f9ab2556c7c1836228399dbd063a417e004937c4e8f1238d59628623dfef098ace7e0e94d254f75cf60370991373e1aaacd754e553d42045943f1356cd63e1f3db516c660ea72208a93925c913478d2a2d2d9e8f1fadaae136ac42187cbeaf41a1fecca67b1d9f8e45b8a21509b73b3cda6895c83a1346812b5ffe2c1a73db6cbccc95409b09f062caa7414f2a7b9182faddea8857099a424d6bff704d75eeb4793e53fd6c6b392efd365d02217bb0a66eb65c8ea113ee6800c30e14140d1db32a753de462a44320d257b6c021f6b2dd25f7507d6320a391d0b0de1d495960f32f421d11d91c7ea58f1838f225e75d81035ec78aae5007a650551a2130d8380a88a42cc5f00243adb65118f62bae9123494c72cc94cd04a9a48393508a4042ff5d4bc0de75cf5827c32aece92426e6a4df489f63ffd696a5e09c4569596d59d11853d305eb433044f7c58f0208708aff7ec7dfba646904fab95e07c593ad93e76728526f8818fd7aa58402a80feee7eb15a2f0b3c4cdb007544c53de0e1f82c11bd9d37ec0647180f05a657c2933fc48c254c582ab7479a9c83bd7c4f8eb0e29efa3ed8c4095fe08a1b447db0a0b2c4929f11cc7d7829662940098723811cecc8af257c9ca591eff5e55f15c1ea2954f52b5693fe6643d48e9f2f23f2143777b0cd7c33e824b1df67327692829046a46469f24a35f7a4a268c8071d3b08d4de93efe35e71b2ae80a054ae6cd0345a6af2d87b666f3f5babd0cd2158f9f1d0454c258d701393f9c43f96de381042f38b49a73be70882662b7070df771bc438452d607e3b501f82092cacd524912d368f485ca72dcc9607f4b1db83a3bd6b6e02d235791457fb5526e68a78d321645077c96714c920ce0f10bfbe8a0fd9da0b9366ee0db2b686811ff10cb381d13e0536e73e9b49c2341023a2ceb8651403207aba6394c9fe86be75b1f3c92342221fafa5b188657ec49279ea611926609c5a0a8b137d58691968eec74595db30a5eab47f49335e4f7b0e1c82e6cff21317280e2cdc9c35446b64b3c2295b7ec14cda916f25e16626a75feeb120e95eebd9754a395186204d03d9241a401df77194d9938769c163b7eda901948cbeecdf3e841abf27b3b8c92903ee36dcc5d060953aff4f2442313a8191adb4c9b374dd9cc5f08e8ba058b5c03dd111f489e461c1723525286162fc0c4846dbc36eac9cb8d32c1a121ebf3d81ccb322277755a2e55761686f53936d703806a52065735bcf568ac9d9adf605be45a56b980d198260445b04842fa9fd7ff6d367f50ed8553de4d1743d992577279a8d76586f479110cf8e5875e0c4682626f6a1e55d9116fcc4bebe725bf5ff24704153d8d7b94", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a2c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851aefe0609e27b1f5e4ba46cd640bb7bbcf5d11024b93b2b80830d9827e59538f1b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4135a40b6144087f868125f387412d514b5747bfc6d2fe2bf59b31a94cba4d7b722725d5e5594a87fd3ee32e41daddbf89c75efb45a474670af2162a31702ad09" }, "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000040ab06fe21da21825000000000000000000000000000000000000000000000003d09f6ba158cb416a00000000000000000000000000000000000000000000000653ab0fe8456da7cb0000000000000000000000000000000000000000000000000000b0d51ec6f54400000000000000000000000000000000000000000000000971f9a84520aa31270000000000000000000000000000000000000000000000069a8682bd694d08990000000000000000000000000000000000000000000000004aac14ee1f1f9cc300000000000000000000000000000000000000000000000000023c403e139d5c000000000000000000000000000000000000000000000005840763058f90f862000000000000000000000000000000000000000000000001af162e6dd3d19c42000000000000000000000000000000000000000000000004606b05f7c092197c0000000000000000000000000000000000000000000000000002ec3674acffb5000000000000000000000000000000000000000000000000d53670d6246e537900000000000000000000000000000000000000000000000f962e8b1c78199d1300000000000000000000000000000000000000000000000a30b52d4914d59ed9000000000000000000000000000000000000000000000000000115258cf5acef2743e93d4e6374c24aef3bebcc2f4f2420e9ba7a5714ca96df52ec420a5812cc18aa6c4a565a8eae50ecf4b59ac47f274431bbfabc786534686220220083c6d426a4521a0e9e50e463baf8bba7d862eb979fee57112d10d6739cfbc5a6fd1f9512071ec987928cbd4fbaf9d86e277597b28f7f28b44afac1d78d050684ee7ab32ef47cc61e6570347351e7d14d514d6aaaa183d8f3136db330f81539a5ff03c021df9c271991b7567f82707a8ad58cc0f08a854d6c66f37ac04f4975ac8b16fa235b7da200d49e8945b4239de3bbf5257002bec0d02ca95619441e75985ca786043564090305898166319bc14b2ae8de7f23278e47910d4af9b22c1af6e4d6772bfd7385b9d88b321d75fb535fb79e9653cec5db75ad0ee5b430d3cf69f3f2e42a8c8cc6a439e517bcd51609fddca9af121f15696115028a34f01b99431f8d2d084c40dd7a68e2440be69d3c4cb4193013c9a52d743256ce32f3ae7ddb9966c82d4e413c9b46708028b32614768bf0ee52c354d61dd7ed7a6c07b4b2160877f005217f14563fcde0b9affa023d3ffc28acdf88fb52a0ab455edc3f200c41d78419495bf2c53688d2c38eb25f9b069fe0fac1156ab227d167346d68483944b5cd02d388139006df14da7dc59c66306bb63b141ffb44b153d3a2ce2be113a26fbd0f39043bbfa987f698498492d2dd5a9a107e44e8c797477789319d1c368f17d12e2ce36c2407c29941b527945fd15b8c65295e960b553088196c5c3efe8909610ac2ed3cfbfb0d8f6b42a55b7d96c1d6b876bb7eeaae3a52cb522e43842b6cf719af9dcd59ec0090750f2b640dfa3838c30b7fc60d524f21ec41e020f83136ab097d308477adc85664b412502e9e074c8ce86605f0bec32cdc0a92007bfacb4a0724afa887cb10b384a0f8a2f435e5a6a8a2476c1a43e794b5d040de6e17d96705a97dcc7110d434ef35baec043f75f16a8ad2a65ef85cbe11992a9cbb31614b1ff0f0f3ea28e6d81f4eb1d6f1370ba58e2534cb627378b8b69f6daacaace2fd158335faad367b8a5f88eacd5165afe29486713ca7d4d0a797d1b4a559d334dd13639843bb7819198343f00f242d17f223126fd1719b0817cf7fd5687b15fbd2031d179543cc51568d8451eb313243d02cab952c0817f78098276b027c46037f05df41d2b0c7bfb60da2364a6be4d4c6efc8a3bc3cfb91bb7800175e8a7049322469195f5e50994463f2ebd5690e6dd07daa9e797b5ef4cab2d13985f03f8994232deb337dd366766174a6024864c57858399fb5aa60ae7ffe0bc4f60558e5fc029d082fd155f55b3b7a1d5d11a9cc8d05db1bf23bf81781e0c95a310768386f136393e0465b8d7a920b5f46d99cfd947040ebf2edc4c9b56515fc197e7edb320d5ec04cff4e878dfda85a4d833aa29c3732cd973cb57044f72f28e220f086fb2394986b66cb4c5448a2e31fa30250ff6a017e4972a2178aaab76d92d14b59841bf47a8f314c8ed5cc73529ec625ee72879c6c70572cdfaa59a411146f53483803b645f74ae55e77770167dc9f8b9f88352d5d1405f2b2e1793f402ad14e0ea93040f6b5948c98694398711f39e7c5083d3a864d51f5d18ba87dc2604e5328931206b74e1af900f25a6159e24249b86e36c64edf56148d23e3e38eb6c415256d073fb0322559c292ab8be3d1bae23947cf6859296a20c33125166ea1e5db893200c783848aafa0b2076135f23aed7b2f6841c5d864e9ab0319b223b3df0dbff31c4ec54ef7cdce4533e6d6646188c6e79e0c62271353827e0cb821a320bedadf09e16bbcaeb45335a89d7dd4053c17ba82a9e0240b3fff9a7cd58af3f70f608d2e354669ea4f1ad977ad4ad13ababa66c1fc2794ee78c9fc4faaf890c9f2328018cf65cd2daff5027404b5baeed49e757db29c96fa3219ebae1a4a1c682c85131133f4d09ce60ca38a9cecc6a345a442cd11b6ed794e3bababd71f35c718c3b21a461b3893f154353b00e3e5c85832e6737f7100a9a5e29e421d31ff3fd1acfc177698f7e90ec5658b4e542ba9d4e202e46b6c576a1d694d7dc59c02236d052a098e5044c0f357415a36fce6e34465177fc0b914d8004ebb1ea7d7cfde7e91bd08cd0f8c745174106bea30b2acb002d58b77a984a681747c096470a038878b2619747a68514f726714f01143ba33c6067623d8d784e916716ff2da9c40f34787207b327f55b17bee72826ac64f91c43badf86e8117648079adc97e81059a5ed1283696ed822c0e5b0728ba6156a0636ad29ef805fd968b0efe04e72d174bc4dc05d38ab82149ca15c737f244b7c198deef75536eb116a6b70c99caf5a411978829028ab6b848af1a7bdf7240b46e78dfaa08f6e20a2ad9d72aefdb9b657184162d1ec631077462701554b4917f1598235d2fd2e728a6296e841f5109749dd4ac20add72426e1dd53e17d74de24f677e889c8dc52e462e9fb12989153424b619a168ef2fcbf4e23612d222a24d236a685e7ac1dee99e55a15ad5c5ccd1a90868c1f16d13b8af94831b9d998517f478b12843e79b2cf8b510e731df1e4e53d85f027ba9b775ee38e4722c1c50d840f864ad79d5b7f211bba5ace55624d9d67bf272750e7e77b265292e81e72b769ac39c69d6b87e0abf8a12905291bab45049bc9003580454d1f45cf3209f216a215f94e8fa0296039d960ca361184adaf05a5110ad6e799d393a4ec57ac1b496f0bd4c2d2d0a21704d2e807c02be4c0e65f71861ad74d91e1daae0ea9f7da1d529adb5387d14c79e9c88b2b0a5ea1354a2c451726d5453a94e0188988fcd6be18f9e0daaf1b3b4a4942c4ffe4bbe72a071ad2500f4019c931b7a61459490286f70ab69c358111ccb1062c984dca75e9be31d83f25a3f36ec88d2dfdba029caf2b502ac2d27ae6e2e4bc2c51310b577be3097b1904113b78cf1b719ea164112ef6f06d8203f3fa089bffa1864bfde3cb53dbc70d05f2887b68f9ab5de5336385f3970ac6601ca216e60bde4fb2727359a07121df2d366ccdfab360ef1f550cc116b3c4c12260c44d115662e16480f4653e71c601301100a4d931b185a9f7237ed93ffe377fb2c8a41ed83628be99e96edcf0ee871fc497dd70ba7658fb904f63552ce36737823d94cb2c86b5d043d00771798b0e137c6b475a99e2fa444f6249c5faded17fd2593e841741993be5e10a0ceb3fab13491b3cf515f2488e1ae936799cf3171105220015ed05efd0df77b02b685b850d4dc8e08ab1a1e4d84a89d55c2009f137b071b5ef47b817e8cfcfdfc318f7ee2e574cdd771653fd4ff74f23315ebec3e844c7fe91103cef4293ec6814eef68628463a2acefb7f863024e93710531118059348f66c509d87a173c331a959375d2d484ab1e6ff5834122e05bbbb8c5bcb5a3cc4f940e46e230bd2eecad42884042ae070003fdc0787eda93dbea86db1bc786f91b8124e8bcc48b53b91ad2743f316cdfb4f8af4e985375743c1ce93ddcf33243d2a785af13054b6f0a8e2d7bdce2c0f57dc1c3c616fa0cb441100f067dfdad8a704b9409c752b6a44a8192af35104d6a676ac93f8ab16780b7311c4d9e09ee52618cf47f75ddb4debfa560e325f13720280111945493a97974e573a977c4be5cd28073fc26df9e47a030a7b3cd60a9d160bbdbcf86a754657011e6221e8d3c0df4cc4f51233d4a587362f34660c209d0ce770afb06493a48ff0188e7b0f2bfdc3290970cfc07ab046a894581ca208af11ceaab5973914f6936e5e1bb5c7171e41a83758ddace922067cbda7bec528a70f53d8652e346f2811637a2bc348fa4a329543ee215284507e18213680c01b87d158aa750a942593d21665b070248eb05d9d99a3e718e83d051b80b4d0572cd3caacf4b04d9b02e7b5d119f825c51ff746f1b90690fcfff820812513341a1e518c70665d56c4fdd8026696ce962b244efd18337beb6d851371691ae548b0134e21cdd57a574975f136c4c9df4fd47242e4e8d5b4d4d5c64d434b7dc224f80ece578109ba28c79b93d58ca355122180703b48e1e65bffa0e4b8cd0834892a1af3698b86a6f19f95d7bb233b3da96574a99ae4be95be5c6a1ac80cecbaf4100cad09b438114a5de5e688f2350b6bb9628a8d529f64a0d03495a940b8af841219cbf4d848305edba7019532ed0abd817e4351dbf89985edd837bb84a253897f0f45cd9cb9704c95846fca3cbe168336a7033b9e5484e1ffae1377e5bc24ac8604d341ff988a69336a5227edf5f1ebfe4c03b8080006f1734bcedbd13c88998b04fd88933744df41cea43cf4364514c189bccdae52b745765d39cc7323e0808926e73ff470ae40e1274dfd6e4f1a1e43d34ba15f6cf124dd408631b349b3288d1bde5a64444aa6d4d969b48fb876fdab78c00be233cc6d7c2285b5d57e0d541a215591b9ba63f7a1031540a0fd2ef4b25c45401435283b8f5714a6593377fef92883a753b19056b11835d43a8dab798ae41ae19bddcc9b77e4a7d62a9044237d25debfe3113d1c32ceef3f28f255fcd7d7c1cc926ef05eb8fd38452db84907580238852d6123effb342ca4edc2e2e6cb1f3503ce645286483a24fa0d5a5203cc1901f6bf1750b0562f89622e7844fe4dc4df62f393e2df9ffc367d5559fa0eaa2e9d24c26cd530248c3612b7b92324fd60e9f2809a7714a432795348dc35e1b6157cf914b2e55748cff5db38d153c4bbfbbea5d69640d6ee322b2486d2ed27b206d734392617d64c10f7e920f8b861f2e2ff047f95f64234ab271cae6257fe270956d1443a7ffa4ed7934245c40547d4f1849536d1a325f89ab9c7068a918795136bc9fc6e672be03de71c3480bbbd5d86b9bf3271aea5ac41af91b48c52898c165691c3be42b9f43326f98e9240862a025bc5635dc1c606fc0f2509d1efef1b0fc08de20191f99e02b70875d3dab5664655fdd5290c12d141284f03b5760975043a240b864666ca80bd0d9834ba077440690d7e32958bb09128d192c4a882d80291a7bdc10b03ac9e1ba37e18351704ce1a60bc4cbdfe5396fe6973993ba8a0217669bd2f4ff5f684fdf64f00bd579194d190e52d3404eb217b7bdb07635a8704e5e9cc2eedfb4d5eac2c8c97f99a67a70b722e722545ae14a382fe970a42ce2fa136aa4acc0f44847caaec9e9b5894fce40b03f53e879fce904b96b2944c3219a345407d3b0b4701deee680e0407a6ba609db5af0239234e3038e2abe9573d1cddb19fa9e9f7cf586724b8ed082e215fdd0da169f6bfdee0c722e449c65f262b2aec80e8ee480b6410af0902bad7c53ee032e7835fe9dd4d3be4567195df882e777b9560db7442ad719eaac80f50ef874f2c54e279f3d54624ee02f47fa9501e264fe1f239017e8a52e0b02360126fcd71fcf44f9aef62d0eb47d6a2ae41e72ccdee6ac3d32aea7fd40afc96b019f5a9951a0b411e648fd40a41c1da06214401b6bd9f4b96db40cd2e3c3999220a789265f0d09426777713470380bc5bfc5e1fbbed783e3fec98db96fbd0ba69cd290c27907ef7b1982e7ef331bad447a1880722f5b7cb793fd8f27b9ed1d31dcae36e2338a2131a928c1d0e273db270b05e2f9b8b2e7e2762b41e67be8ce9aff152d20108397f3b73bcffa359b7f1ef0d4a2e963a4898c66d5706967babac451f8be3c3576e914f059af6ec9aa0952e5753019bd4cf18e64290f1c165856c17f0ba9651543a42a2ba95777c40f383d5d5311e747e15b6bbc7b2270975e0fa26e5a46291cc68074be35e4aff55b3e5ab75ea0b86348e389c8c5fd94f675becb39304de9b7aba1f5f24f9436542ac7270e91809379260ca1721fe623ed93c31cfc73290184e3fd5be777ff9748a62635aafce2400b92b1237ecf7292a17c5ac3b3d49ce2c996f9be7c74022ae9328269095f4261d23c6956f89f4d53b4dcab088be3fd495b11761c7738db37914e27d4ae9a02f40c24808c10c618690498fca7f59fb1d4ad4900940178b7cfd3c73d8a0b4a6299532a13a9c4aafe6663eedc2abb30f5dc5efd034730725391abd53c9b10d0c2e83c607da7e98c97acb9a1f579133f4cab156b61d5db4d14bdbeff7f42c65d419deb1cf3760699ead370c8b82510c0b32c579ed97d96a225c1ab0686fc6130a074eca7416092782b3271155c927f47020e8c0f1323f683cca2ed574050f733001814f79d7e5adab32768514f5804dad018d2ffc2eb9339ce639e4217b7cbd59290c6c55c4161c0fc95458c7b2caab39bd0ae622b1cd31a6facb5771fa91fdc61b8350d295d4a8ac29612ee0c7c58393f0fce2304cdd04e26b903b989a890d2512f3245c84b74f70e541548f1b43103ff12d4183ca15f6c5c4953149159f688a066190fb34a540cf37b4b011e5d56d702be708ac52a6381e6c7c14fd5e2390bf0f02054dd47b7c3130ba3b05a8358213791d14abd0942f197ff41acfd49e25000029febbf849756d5b0a1ea245c8e72a8ff5d8142e0dfbcb03d081a1e6aa523c0b9be309992df10251df3500bbefde5e398900b31a2873400259434818825128009b76de86ffbe9a22f6b0401cf2a1d31b9bc7a467edc49d08bad4a3be6a867e0369d0df4228f24157e3e74bbf98cc4fccb350cc0b1453985f35bd6d59eb3bbb0c8a708fab3e36564c0fbcf59b52b07d420c6c27b21dcbb404d78ea17ab65ab22a7ee682d495e473c0b1901da9986e35a66d58b90181b66c648ae8a057a0c65509d48727dae811979c6a30df9bb90dbb3835baf0439724bd6a16546f888067321026e81944747f1c22e9ae204cd1991984a23fcfd5310572b72d74248c91be8f1d39a8b44143e8e86c599d26faad8bdf1aaa2dfe0a5a6bbd420fb8cc4827abf30fe2a9d0839b22588fd2a0dc6cfb92fd6f86afd7f9058f6910f99e65a315a0c106958952ea6ca6135d6b09f6e67571bf4a4e4641f96965dd1706c572337152531aa504225fd8ef9d54705af04553d536d3dcfee38b3de834864252be74304ea8088fafea00475b0ad5d40f8ceab5961c9d626cafc84b26f84400357447687096293abc23a2e828c5c37c97c38f866911d9ca984a03bfa86b291bda99435500732ecc4388049288b6b777af9d4758126f2a552bcc16e75bf97bf596c0c497237f1c9f0b1ae3d97bc4531060d3ac576bc01cd9d5f10e137030e4bd33a81e16a8221e79ae12a6f7971c03de925cc87490f9b64ee9db4168f1b03d18f286ee8efbed28f9b5457b522f7446cdf3390c66ff86bc466884af706dec76517f9be11b1c4a24ad702d86399352c2f7ad293ae42d8eed275aa8ffb377b3c4f2d547deb234e62763ff0f3e58cbfdc39452771c14cbc6c8eb0e6911d53ceb304a20947766d8b415fdcd1099e3d5ae5021bc943de2cdc11ab57fa9b5a6f6b063ce886aa3eafc5d1a518044009fd94014486fc043c811bf0ef250d0ce1a073653c935db8219087c2840edcf89f08a32eef607cab2cb9140e18cc0cdcffcb023e0b26ac6691e290e01def66e7b0a31bb1110291cb92cbecbb0a5641fb9f175671ed9a114b757acc6169bebf9c9bef1c27372d5509192f4dbc0999b57d6501f2f4e04bf5be97bfb33138ccf7f178db34be5d750cd2bfd8a2375ee0ac34b4410896ad8875efb8d81131f2d5c591e39c8adab09adb775f559b695f390e934af08c8182757cf340407450bf3ab02aee67a410cff649e08fb935e1f6ef6556706d6f02464125ca1c6eb8a2db6877fb4822ab722f3b8e9bf03be6078fd699fb51125971b53a39616de119605075a795d83199b15c0d103f0c91f11395a517d1c83340c2c91aa42663d1a3e071e4414d800496e2ee7d8551acb3426677077be3c35d7f41c69d4bba4606ef7036ae83ef1b1c06905b50b2fa351b94d4be937016f6d8533368481481ca6fda30304474257de875ef5168fc4565468471c81b4c65719d4037d7cc7c835ccd80c01de4d6a992f902e74fe1b3195f1ddb53a672b331133175443811b957c1f68090dc846cc9b717234f1aae65a03dc471461efcf21e69d8be21937dca5aca848c90b98782989e7ad92e85b4f3ae502fed8f71ae2286d1336ac0d32b8a33de55273139cc206d8ffdbfba5df914eba7251435de570b239374f98276811d3469e45771db82c1754c29ac6c6e39643b34feda8dc704facf77b1eff5ef2fb8e7aa71def11a5401416d16223009f6de585be285013d644bb03ee8d9e124ff9ecd139914a2d37d47a97ef260781595c5a33bc089bf5b870b31c2cbb6e2cb96551d08fe1f100f2311a43fcca3ae43aff407034fb1b3ebd7b2a26127be42820fa97cfdb8fa02f41c7e12202331d288ff905e07293cde5fb1b4416853bb5c52bcc2120a445072554185db5bbc6c13c023e10e818333b3ea63907bdcf0275688f4480a8727b2f183dbf9242fb2dbd3320d2196863f6fb29fda90d4d090bb3afba4a59b2c68a5516cde31823fcc0d15ffd5476a12e54eb6c38b28d04a9fc2bb41e13ad2f1f2875138bed8a04299e7c714f7413e5f053e37e304248e7d969a4f994d15410f15c2b1d2aa31f2d70042e1d242c15c593a7a846a95fc96ba029425aea988212bf900e0e9b2a97f5f8b3549fed8148d271db03cf878b601a6e694024aefcc8ed72093f03a135b5fc9b72df85fab4825c6352711b7a71fc9876d66ffa5949500a6508c52518966b85d42ab90240e68488817e62085f222850ee38137f65ef5173eb9d341f47eba397a1ba71a4509aaa73f535b5e2a771a99a080ac81c2632a3096b287623a2779126dafcc6577a643972a6922fd3eb24bd3281dfb6dc1d19cd224a269f013a2861c8b8fc8036d89c1689e27a68ff32162685f45ee516f55279ed6b32372a3ca326977ec6b34cfb295fbc099ed807c2284bf81a78546681214ab14c4edf113efb51ddbbb7189ce15790f96f54615db145c0ce9e5e818a59e0ca7130a6921b9ae2514327086240dbee82447f852fbd5e599af80db39784af850b88d9060026e9833fa6c872b2c1372f93099606837851a1a168c48145d9951640a97b72092042dee789eaeec2724b826000d39dbf75ba4710212bfa31068bec37ddc16a64050095288cf1d25c9f9489012d3349074eec75085803491ebd8298924143273d1c498547e97386e641295ec8431522d17cb9dfbd2f2b4217cd83c808c7c5f18d10737bcd0f8950e8b2555b78e3f78e91834a37af5386fdae725e3f20bb54030e189b7db9b6e8ec5eff63867cba626e652be6c46d665db454445f0f83f08084af2113e5da93928627ef87936d16a7000a0a094a5002c5735caed7f84e96fc8af1114de25f98cadbda11d3a91d4d182424c842fddb7c69cf66ddf1ecf33311c9e91b198867975fea5bdccc98d7e30e8f3c8d8a12e2cf1da8a91da8373491c658ca0ac6e1bca3d1a63e41cbcb27336f3072f08b53d48c7ab0d0e077e2aaaf1ecc0e1c3898c8633a490bbed7d87f0b648dc8f0f18c049f79f8510814b0513f9716542796fad753e5425f502639f66f85280d9a026609045e5ce3d85d58cddfa0760f2da4146c8424d471d5bd5992a8fbc67b3bc24a107b83575ea4ec667c8684d4a50d879310cf292b4176fd2da386d2827c6a949c75f45c1af3841c6742811f4e532b67908e68172683560e3ba9deff96afa53543c7525ab1e2572c675585600a3c0e8d9cccbdf1d2dba272332cfbf5dcdbf4598ca883d526f0348b407a0b53b7f72d63519b8a3fd6b14bf1bbe4d0caea0eb71f6303aeaf5198f0b96c540561c7b024593b5c3bc10bdef4eeb418bd7d40e811b46a10d85eb1d03201b1174418ebef2f8d48c74be847aaad4d92bb1c1a4905fb0fad9118f778ded6e9f18e291e974118d6dfb276396599d87ac39e61fbbd9acd173234149be49b533d3329a6a471a00d06013725130cab4546ae6063008eb76f952a8f7b1d4bc364948346145a1d512950eb1ba9ddff18f9a5a6b46dd0375ce99fa87e59b41ca52fa1956a5f5b54851cada72169d4dce1449ab2efb0c6b9ac9e152462a4b94938d0e02d656b2b302516047fd71cfbc6fc5f71c30e004e4e77fab46d560bb462bdb52508506099fbda2a132413027e013ca6de54dde3497cd6f4e165855c4daf55a23da099c2ab11422b7037249598689857c3181330d7c7c0c85723bebf2fd38ab79ff15ab71c736e070cce892a0e995f3eaeeafa42934a50cd1c3f68cf168c50c2541e7722e08b5a1952dc2def02fc665ba4ea42f9820ec05e6f622ac1445b710685b911d67280240ab866edeab9a93f511c74e06d9409f43ea822de953e04b405e4e258f3cb641e1d51e5848cf19ba0090c881d58acc9d09c8122626a807299cd251dea77bfdc6c1db5a608d1d7f90441ac3da7a850b39d37ba055d46822b7f4a440b369ee3dbfe0e2e4afb5a995b21b025423a3421ea55f22a69812efe31e835d00ffbaa26115c1e6fcc35e3bc1ecaa89b717d2acd42f0ace83fb1e88c08230658c64331a0d3c002205b52ce6a9a3afcfdb7fd07f0ff917b0682a827e6be0e519de443bd7855c028ec2309d0e2f5f228b984c5874b5c1ad10d7bd97a10bac7b9828e46ed4b5a281205f36b20d02c74c43cb5a415a3785ca8bfb4d3d4d2ec5816901120511ac4f82ddf777d020409db617ffc7d064710afe1af7171ab23eb3a2827242a81ac15a00c068ef21b34310cc14da591ad18e6e044f3eaace0ee07bd05ecbf722a7b6c292bc6f2d578c6d7d9c998f997dfc03a913dde3e1d965b17f32593a5a0627e0c092c10fda42bc30f3ce904e4c26eff37953802d60afce5cbb470bb6d15412e268f3023a4042bc161e38fd8e621f9d459f3effe4110dc22da5fa1868bd3918cf7930d18599d713f0b6c205283389b0f2453340975f771defb2b5e31c3857dc90fd92daf36046a3cb64afcd9050f4bbb9fa89428624a066a44dc8693c560b09ba22b0a418603118fbf9d862084533831534afd2aaec3cb75aaaf22b396d7188bfb0e0b3709fb4ce0a08b7dd002572a13ec48a5a480dbaee3570d1fce629dc5d0e56b21cffbd42d18cd9b62dd78c23ffc8d2f5ae1c467b80afb45c687c9dc954e8f321b43ddb100a18f3af3880a51969626dc3e15ba3bc4980e171184ee3bd89dee052639becb1659d8c12e42e0652b6a33d148999ce6153d4f4e7afc0e640bb231ca2a59dd79ce91efcc51caf5495bf45e00df2beb4120e7d30d2f5a0446206fb6e3196ba3c835908bd1a2bc0754ab6bc4819f2f5210fa494f76792070e5e2c454be02b1da704e87aa8489ddb9340f8841d94446eb311f21bd78713ae2498aaae1631834d8fede4f068b2a340d75f9e37b796a0d014d265179ae4f24c0e79811981d2ef1b18f66869e914aef1780d5bd351e58eee8423b18cc9314079f6aa2f344601c8b4a05c7631d77da06ced0416667ba392875d9af73a3157ef63673783dc13826a144f350975fa8223473bce2d7337890bd3cf3e56eccdaa2928923b06eb9f5178761d6ee4c5c09d2c2970c06cff0a9baab2d0718f5db3f61455817fe60a7862f8573382c9e2d1eb09bdd03bb2af0db6344c045fb86e12579a8205b460b1c0312cf88883ca725d6b7319535c10442a31afbb47781e7b486a30e4c35a016770c23790213fc854527c7dba54a75a9de6b88d741433c32774d547ef5e9d86d1d76125a9c41bfe6c1c5414b4954c5ddd09c8830eb22edfa6888f76e6f227358ce321c8233821ebcf0fd330a038028d95c8555be7bfe7e30a6e9a95934ca77461df027d3e0d2c5716339668a42412bbbc5e10dd51fe22a7ab706d88361503c99d4ef0b15a392a838b5c3b67c251972cd268b60c15d3fbbd6bebd225ee236d219d942076f9d9bbfb77981cbce154b5b9b55a15e74416b9af9b1dccc957bafc744c124277d650e13f2839ee6faa36b0a3b4db8597de30e4b95efba2d6aae287df1c67b1eac34ac25df0fa2360d47b727bf22d4cff0745872825394be099fe22b57a12614ed7350f7e16da6c81c07dfdec5c33efe7b6486e06b96a4659ec6fd8671f6a82c55903d7c139842b424d91666571c68928cea428bc1d612d6e7f44bf7c27d0f17ed583db00056b3382cf91233026547ebe5f93f1aad6309cfc2e419a7c179520e99025f7f20d9951f7b11b0319944d7d18c1636bcfea5f034be45a58632372204770cb1ac257d09f1a6a07837916260885ea279463d727b331b264b1f0b49381f7f2e77f8cc6a69171a6a7ad635e415ca81bf32489b92644e873a4fc170ffe1117c1dfb8f1d10c543a1d9047564d769469816930e3f41854864298374980cde254352efbe0abd397db0fd78222d71595961d9981c996d5ef96383dbbb766b840876fa3e4dbfee7486a0199e40a93774fcc202ca7e094b1bb99bb5c03d5128a626564e425bac7f3eac2991821756e078e406f16d5fa573f59897d0d345244d590b3d34d64df3afc81bacfd30b9e8164ed6e043ec5250231e37f9a6a8f652bd7f0fbaf17b908d410d6d947d7fca1d3b51ace99f755928d9aeea46034d972a296d25beb92b01ebe662acc722dc0eb9b8ad40227e67acc989bcc820773e1c83aa612905aefce55491f1925af1c04f0f231be3514324a179e5315a1bb37dae95a5661f956272bb735d7b95e89c33e7c01be2b7e83582ae1a355e9df4ecc315e84dc62ab2c1206457dc7ce8ec99f837075700053651b11109c8627bbc53705b593bd91c3da4df434d327d486cd25a916b8607d33f31ec6b9e9bff15eed344c0fe88280d877a5b4bcdb00799f7837455cd919fddd991bfdd02dda98cfc21db8b9b880e0fe664cd7591257dcc7cc3e0c131721b88d6fc684e93073ba5d5b050eba773ee1f1917ab154ba311a46e1de3a8818934b0984ceb63d6f8a0e8fdd1e88f8ccb8521fe6811cbc580e23c1ffea1b329d7e51865a34d1c0fb717e870a221cdf9390a1815630d082b4327aaaa31d4e2c343fe24db2ee45cfb6209e7b3f052f65ea94a05769a31ac312b65e8b702576524d2e51da83c3511a1b211e67cf750a2e2a63a003f5e68ab24af990d658fa911e9b16d48e2dd4c39a5420208eab00611a2952c14a3adf3a46fd71d7dff35d4a329ae6edf88bf82ce63767a264c0f55f58152e12f9215f4f60bcf23a87a052d4ff98d4ea15397227ef1a31e27630c2598afeb781697c705ff91abd6ef567672b21af372848a5b907b137380b6f35578fa10c0442f8e8a9c1185d5390101051a7bcb9a9cc26e54dfc06dce51a827d771e712c4e726a462eb20b0b67b110f61c511ac1d1768c17bb76bd94f4687e926e91d8d326a169341d5a6c63d0f4d2a4657014e2d32ef9e2605e83ffff451efd4e44c31e4331cd7e76c07fe587393bd2896c20f496a1284126981e6dd39c9a93489dea6aad722589bcf9152050f6fc89e1c9bdbd1cd00b14c92dac4b0888dce9deb64d34b6e2799104440d4688bdbdf16aa80a0032eae6ecf4f5457dc7ec502f9d6541e57f11d0a33df49cb75b224d68297662a24fcad6c6c4a1d058b66b52e5194be351854214297bd50145c5117c2046294d80ab362f32cefafe752aebb35e18b6343202b088a5e3740209e74a2dfe097771d91abbf91821bd6d8d2775370bfe80c3b05a20b28e7e4a875a7f7fcfb34543326cca4cf25a9711579795fe1cc240533bb43bc25531a47002615f2a582ae9b97af5ab449ee49ebf1524fc70284c5db9d9ab8d60cd6a756b5b14120ec32650a922f09b9ff0e05191d9cb64e8a49bc9ba9ff38862f8438c9f72d8de501a998b72b3e094cee71964c093774e6cde95231bc7488af29ef82fc95f20b73e22cf7397dc2f815c98cc0ab35ce3aa2573b3bfa52c1c4e20b14ce9085edd1ba3efd5b4e7eae79db51e0c0b6eccc2d8c2fe017b1e953050b10865d5f69252f27099e8607385efc6b34356105e0d55711c256623bf827cd6601ebdd0093ccf8b5b7c22a4a7c023a8f3f6f9c2e033af6e983488fe17ef544b70afbb9a6c7baf14c4c66d617464ae680959258f3d6d0921d8220bfcdf54a201307980f3e5d8997783a5cc6260d02bef3906564b64e944d925cb366b68aeae3762bfb562400014dcb231e337e4cb1835d935a6ceef1cb72c2bf7ac05b9b7063b82b65f37d1d1f7836b8339d8b2588c630d786c8a0cf090161b1d37814363803581fa5ce82abb747da6d6c34a0260e627bebd1d4776e9e328fafb6d1383fb9b2062ee54c659b246a40a81b5eb1901216be06a9a6da8aa2fcba770d5ce232f8bb551259bb0dcab25477febc5d2db3124082c3fa025144e7ff3a2f56d6ba87a7d2df10fe1067d51a1356857e5a675b99f2573747dc685678c1896d6d820d5fbb6a142365320332eba438ef44aa656caa7815f18b3f9c59eb23e8b60deff463beccff1f48996cee11a6a538526d034a9b60f065fe0f5039bf073640b2ec41db6fbac5193ea88028976c924952c5225f55aa1affac5e7586245ba59dd8a2df0bc13af700e67b6fd740d52b6bec53ad840a94f31169d6dadbe51fc56a45b3fb95b7363e06027ec79d84a0751501e4ce5afbfd2be1117af68a171593a2ae7f2517eeaf3d0a26ef542845f9179278f756a8155454f79686846fc0527bbd8cd9f42ef1287e06eb851df7c68cb72bc28b11fb26f5a0302f4d1431d9bfde843ef7c022000d9322e65464fc3fd94083d6b1d693fc9bcd311e078cc6fb4bf837c86b1336509b0c03785ca3629140210823f3a34e477e6b53113a069203acc49a6df56466be294f", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000204570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a701de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000a6a7b38193b9791f900000000000000000000000000000000000000000000000938adfed18caae40000000000000000000000000000000000000000000000000e21f5e071314e88420000000000000000000000000000000000000000000000000001dcc3d8161f47000000000000000000000000000000000000000000000002e46c7ed7ae9e45950000000000000000000000000000000000000000000000006b452c19c9c3a95200000000000000000000000000000000000000000000000f937207f5e9d7f27200000000000000000000000000000000000000000000000000011234ce13989700000000000000000000000000000000000000000000000bc917c80a1df8a9de000000000000000000000000000000000000000000000004158d1dc6c5fe0793000000000000000000000000000000000000000000000004df57309713331b9200000000000000000000000000000000000000000000000000007698a06035d700000000000000000000000000000000000000000000000b5363c615bf3049790000000000000000000000000000000000000000000000084836ce0c2626f54700000000000000000000000000000000000000000000000de7f5f0f37ffeb22c0000000000000000000000000000000000000000000000000001bca3c66a8927213f200493e130f2f241df6f50e717977080ee976b404ea52ce9a1739a6538942186c4ce5d408bc4469b54741acb17f81532bb104e167f13788ca258657e19ad18bdeaeabf611459f9516a80aa25dae22a716359feb8721e6f73ce82c65ed90504097a64060c9b98a318d0db5087034f8fb8be44fa54632799b74281b02fa3201b5874dca01aa6c15157bbcebfe698062bc743018cbf4c1838fd88dbc7725f1e2cc9baf93dc954f7630b60d425d36ef81eb1be7d8436127e0444eeea525630a31058031e3a95f311de89e96eed8d8edd125cde39fe180ff58ece8d79e1d4b19f2d6403f1677a3e0f98f7d8669a44ee9d1c5227bc6dc8f7fab18b5ea38a23a8a909d6b647f178c12bf5765e4f3fd9e9353f1578914f6289ab3626968195e2204a0b75d68cafcb752224084dafa2b4ac94248907a3cf380066780a921c968cac8c1745343af4f66eb1673679b9c00300c26624904c97f542932abd632e9d9c20b613f65677439219bc9826056b26cd285c1f6ad8cf48940c417ae82c3b6dda3ef601d957cb411fdb0e5a236ac0758e0f106e5c2aea4d997e4e082051f36f37a07e193e06fcb0040e75764cfe7d7b19379e865f44cde8c4021fe7a2cecf7276e482125b04ce7829f541ae0f786317799e17a5d19b18c35032546d862d824f01aa2b2ec87c35f2af31bba08cf65076c41c5b2a532fce570414bfc6619bde14f1f90519c766248124f4e391908fe40e4f4c2e47d959872db00ccc6c66104be6e3a4a01509488b91bdae4548075814bb3cda9893c4acada580aa2c29959515a57de7a31cc4f2c4335d29fdf50154c3993f6ae9031d06f0541f0e718afaf53d83631efd232f3df1898a0799134d1f263b0bfd6dccf19c72088d8811c5e361ba2efb54be181628501e2f6edc49dd9c2140408a2fa36d107456bc9ca11d66c1e426b5c3300cd87deaf468a61d8cd99c96247bf074714476fdffb493aaaf976d4dd235e99b154e79b2ff5e50ab7f8c6ed0796649601b9c5b5efbb5fc5f5b4f216469ba83161e04612689209e87667d0a819663d69a261da2a789ec0318543355b471178ee51394b1536f2652a7fd5ae364f01c53350a79a0a2c030636f3c2b0b1a4f11f8f70b7dd2a2ab687739334dfa9d83fc290414ae7a1369b7b416834d3971e6e4d10c1e739ee4f5603547ba8ba72746f349ac646d2ab09e4faa5ab7e68fd55609c6fe055052cb8a99d490c94cdd0e20fda8125a7fc867867440b977632d440bc72ddf2a79774bb4346a8defd31709e0d3be983bce16ec2f1b6eb0d064df61d33fe4eb0c0a9a31f221442947a6eb27a710db271537ecb73fcaa4ddc4435b85207362ff13ebd5a7cea692e2c11170b8f0e19c3996c9d4fe03f579bee10705ba2cb6a64120e3ec3fd8a3623c614a7233e19fd586b3dcddb5ed038a7b87085bc0d01f35ac0a910d04789ce430f092f249b8a95e3f3e9e57d464b7e39b11b0b2ce0511a53f06732a3e7d8ef4a98f1c0197fe86514ff68a832af68a50812a33c5b1b3d7357f2f0fcac439af16b2c3c51ccc318db8bfedef50919ca1df0ecef4cc3a1295d63a16e24b4427e5397d2aadf907e4021dc59573232a6851a4772bd0d7a9511e5e940c45c13faf58cea6eeddbb0a5bab2b269c8d205cd66a41192cb1a02c691451c60cd43ed39782c235f5b7222db00e52277c1bde022d68a3dbef6836899945390002967f653a03db955a5c8ce104f9bded599366c20d9e4e10815d1676c7de75591876c3b768ca0c50d93bbf03fbebd73298b550e6731cc9df74ce6b043d391fe501dcac5bfba80aea4039869994a026ae689f9fd7bbc45b9da35927b07a1400b8050faed5f52a92daf28e2f7a1e113694c5751fa2819d425c7038381219f561d31ab37ed6c11585362715d889de16fa9c110bafe87a89fb3437d64c75602257e3111a8aae71085083c00061af68a50a444442c811ca0702b1ba59efa80707a12e0574243f7c35ad11d0499bf94e73ddb1a0551fd5dc7137b31c2cb4ebc6845b5c11c03ef720b358f0b3417c3073e3d486c03aebb7b020db1ea2ebd23e08aa22bd2657df69bdd20d3023feef2bd7b0efde8763523aae1b31d1bebc86a1577122722af7b9eb04d62bb5d2dd340cb78eaedc412be64449d0314cfaf30771d59b4913156f4b94b14bf9ce19924e16e516ff1bc66e6955224645525d4eb36650422744014aeed42b2f48def893f4b59685a7a00fd42ae1856463b2ee65aa8f09c21def2f6d4a920159b6487b8928519e70a5adaf40e615c04e7b549c276109ed3888e808c5f4a847d257ea6478a544aab60e354c2cd9251e4456ba225db340d3c35b841ab4876ba9361592e0e2f351f1cef7a81e5875c24f4deb7bd8720b702b4d02941825cdc9b2fd2f8aa1e13b760f9e5f6b87dc45dd74defd1cb013a8894bfd211818a2926b858bb7f334bded04473e5c398fe186bbfa744b7c667066c72db7d9a32d8649606e140be47dbd386381b1fbb1a6238a8c38d9346a624d27d057ca8ef71e68e91fdcd8345fca7dfa89faeebce29c454669b769baaaba0fc712dc541ef8280fe2e79458e28de1f0312d7a3dde0572fe68bbf86f046faa8c2e07927b080f28ec7285880a3c089913308ed80d9c983352b34317079dc36f024c88cbe698f1199da4d50f7601e1a92ce7e958b907c3d08ec1c966d10f838864b06cb903a26f183dff4905cb4bd6861c48fc2e538b3fa22385197567809a7167ec25251e71811b35520a843903bea42f31b157ee343aa5335adec9bcd3120a6d1e4edb158539292537016521f1e1719774bc3472bf8cea73aec615aac019b47e7d879642f462263b44a7a876820f0117f968f8b0bcd1478699159b190360eb331181a2c04be40bb9562ccf1693476d7beea398beed58780d39123f31c1c401b3e59502899dcb070a2498fdee2da8d6266a91ed4ff61d016eaac361e643de776750e0b5e0675313820988967945cf62b69b46bb5aa6914de5556c053dd102f5065ef07c881fcc2858d7069c1c6f7915d99118c1d498f3232b2fbe53f19ae68faf49837c761efd1110cb2769cf48c492062503b64b03bb339905c4871ba162089ecb07e88f922b23a72ef676abd989368ca5ca6efaca4a3546caffe57f71aa15550539f41efe8523d17d0a9e67ea0a1f0e79adda4f7729011eeba0d13f56e2e03db3aa4ea0f1b62ac55a22371b0798281faa7e0ebffa3f57ef9db292b0a30d64528ff64c693dda2a3524a0f84f2bb33871e969321e34c0af763e3076e7f460e92e6cc346eb967e087fe895a6acbbca4f7dd6b474ce3421a5c24f07129f17b389059945b911b318113acae2204d6152a672ffe6be7699486fb5feb674a9fa1d63454eb801e0b0071570f519abe7972bf439e8cf28cce87ba571e359329415b61dfd9605d2e5ee440603623c89bda7c629b4a2dba72f964bf3fb0a45d364198059f2bd2b10ffa4bb0a422889998c100754be16415e5a16450ec06bad480226610e78379eb5be4c2f04a91545a58c5916584ce39798d9f1dcf7742481e3fc90226b8a508e45a3179b207ca949a9e68369a4e7a0e259d4706e083f51f4172b24d00940a5e40c5041721c4e2ba1ee11f1bcf9c2ba0876dcd5bc3b74e779fe4fb8318c7406d47ba5b7da0d76ef4979ba783d83ebf8c7d834f6b0e244320263afd46395579b9a73d00a400646144e651a725f8d554dd3612862c70c98fac4a6dc4e7648ae1c99498221430bb83c7a9e1667315b09ff19d292693ca49c81463918d0c8f89e56e7f1cb588f12100b03aae55f7fbf686c6d8fcdfd2ec95fbde59c33ef6e9162c39088034e3427bfb892d72474b03a197db7242e68fce96b631564d2f94eed762c1c722e38040e22909f91577a6ec1e2c330e5a47c8bb5e6ef018f1a83388c73eb754ff32cfb0c4d38ba8b27885ddadf3f520c796d6fc499fcbc2a3c5a83b97cb77a5174d60d2c1234826405656d94521ee61b88a233af7754bd82be24ee44bef1648438d4fe075b2767664640965dd21e2f989bbf4f2f69c5b1d0d1ca61c90f1ccf62a2f766251a5ee4b01447113c913372d0d49d6415b7a479dc7ef064c184b9140d8bee6a0c1089dd993d092a9e654da9b9d13748d9ff552c1d284a9543151223275818ac1bd3d204262544f9b536b40ac64089e0ffe99370db2f5a5303b14f7f1b43feb901c4dbe086dbbdb2f83982b5a415dbd75c8eb212760574f15b9065a39654e17829f0ed29bce220f0eb2ba77754697a8babd74c5dc104896f4bef310d99a77532076a118a429672a66d8dd32e72a259623f4a330291c23a0b39b4376abd57c6b92ef5e2faffdcb6fdf0419185e0528d35366a1b865dc91dc4a1e228379b219eb52a47d3160a98e6bef560957039f8687bff28bbd1654b0e1a2290f335c53f0e0016dcb8f0a0c2e0d7bbccd7415da0fd8725055b26393b87f071918eda8414cbfc13d7d1c5beffe174876dd3ddae96fa6cdf87acad3ba1709dea8c80f4c7d2e4bf0e894e78471fa89f727f99c2073b57ad0ab2b23ab429995f9eb947fdb176e28c2ff432eba5830407d95888ac9843beee5f7c224ecc1a40afaf7534b5c3010aa00d026e74c47892b4c803ccba015b1341b380f9b441eb79f26c0b1c8447d23f912dfb9d2fe28a5cefa496ff3dbde6fc9fa6767339c8035ca97086d6aadb88b0dc1321688e887d98b25cd01819655e9b1432200c665a0a21d40e7e0244f1c373a202aecf5ee088442b4fd2c5313001a02f584d75eb7b6e52776bcfe474f016bd692a2062da16361e2626c90db4aee731753bac3d68a2963f02fab8f43d4fbf52461e1737999975f52f29533837f361e89088c3be55ca975278143f7bc986da19f71c483e99e599ac5ec9c532fbd2c9dab0e088b6978ed7e65dc278a736e5d03792040ddeb18cea5bb841cca78a2f449e0b360ab2ce1f1dd4c8cb39ea12679615111308ccef9632654f64e19d97906c95bebb106b2700a865a5165b9b8ff470a3b72fb6a7f3a91f58d47828e155cc94dcdd10ad68ba207859b696ccd40783fd81f814647a611ec4c8370bf8c01b1ecc618a945f5432f2981b1c4b34c75077d83d252d91e6be4bafe7db360df60b17cdc52871f581610fd0a35442b466230b85830d294f38eee8d3f464f1191bf6f3697840817fab5682593db32bd18c3e35c279a623cd32c6a3098f4ebd0c1b8944809175090b273ca8fcfc6b2381835cfef50ad7135204c539b5ef18e1256f2107d81daa0665d4e3bf06f0ecfc0a1cf514d0aab105a9eb9f695af73b3ba0df3cb2d1c46b7c02bf108567e6d01cf49e6d767d078d0f7eb40d460e34d78692e0114c3f260886fcc7e00c8b3c7008ff429d1a53aa9c2e2a41d1cb34e154057c4686b4ad1d704d5fdca331c9f8168d21ee75a74a465224f3327845653d52575e720a797cda03fef68668ce7623ef7cc9034bee94b8b0181f2b11ce4e8710e7497246d272de79509f4e2fe6c8b42422f866c5c69f6fd41d41c9c326912a2fc919382d4e3d414ef9fc9ddf546ab52c8d93afc585b420390737f26228865603a7ae33fc8e19c944994028eb2a20b57bfe1a2f15f0f372fd14ef2c297e78695908b0d6cedf144040f14908d59b7ffa58b1b1930bd65270942bd8043e38037bb5213ccccb00db3df8a661e6fd31d165ae465810350f8182320df72bd876d111321de34fe704b96862982f670d697336dcd782eee7b90ebac50dd31b62a91af03d377532a157b4262a7d1cba57fa045d75bda66dfc194c0b8d09ef91d887469dafcae262b0ce0c2d8876e189885ee793cc622d42f5719247341ca89e1cdd4026584e5087558fa70f750cb3e58f15f4655cb2aa5c2072ab82f11dd0b47a22d769197a373b5a799afb067f275a5477a2592432165fbe5e5fe93416f0816a98599b5cdb93b09175bb78f688bfa15eef313efb9d49047188736c9f096d86d047d951f18de0d5bb2b435d9cfaa88521fa55553ac1171aa3676e41ef1387921e9df4c7c6e996e54033a9115e1159e9cedc6121ec9e81a1f516dcde852f17605c68e6a41ce312832b731dee504377e669986a4438985af9523357a9f41ab6b9d08bd5e05aa971a009802ce32e31c29228a1f314a2da664067dae295662923d7715c95afb00e860d12d7b168ae050cf29e4bf1ca4fd61b9129794383fc25db22ad3229fe1d00f4761e37e897caa298944185e893a6bceeb54e22b467102f2d8e3e1b2aa2658687b4f52d165f059f4053284f6f3797ed016a1cd51d6ccd1920f19badf516351bb00b5ac3bf3f00da9600778734dfe59ff118b80c46662518e010c4e1220cc9a38032e0cfabd5a5a5da75a1a827179c567443a9f95c14710c03c74cfaf3ba71459a2e6c84aaa0c8dee4d69514795029ea15228a4696961201437035923818557408cdb632f2206a5d9b67d82fb00e9c7c62a3a47c330f5f0875884e7471a7c2cf9b2ed4d9573c90e0ddd52b583e9ef1b23884e89a0c0c801599f00ff79b96f561378e455584cde3a3f5a4c84a86cb62c0998f4f6beba9ff1d9cc5d092a623abf9cd026ca12b2c816c0a63ba7666422d2609f8896501f94116a4ffe86b44b683ba8071cdd9b7db1f7aa2a76e30c1607e8a6aa03a5384ed982b6b6e809ead534b62fd2963a7027800b08e53c94f16a1b00b753322db877ab20fd39eb60b9280409a4e963c6e5ef6f66e7f8713a7690762ad71158a9c3698d42fce32038142ee603c46b0d6510f279e56c2b05d7096c2416cc3dfba58fdc0c50a8b5d89048bef8ad00d7b6fc744234baefdbc29faf5e4694f0d0bc87ea2397a0de33752094611077ec049e3deae0062a3d54d9c55d2986f356ee54d7ec2645c18012c3dfccee4fed0bbf219d4cdd93b94c2f1dcdd964a7ccba4954033a07815248455ea2bc5b096f90cb81d4b534a82a8c55f8df025430884c395c2992128871e87c67a24158e7d9f2e700b685d9450e5084256931384cb517ce80ecbd955671b13c2855124079a3f8a423b00e0512ba3ee812ac85cc596ff81ccfa7c31dea015cfeea3704b8289741c066bf18d60a8f6e453921513d10a7494b7a8ed8bf4430e906fe2dca5b1f2daf50b5151e78872f7f971fde706bfc267af4d6c71c43c461b331c16f81c053dfc33bbe0973f91e979d96622bb2805eca8bd1ca1c86a7e4f2222e04ad38f0cee9892e7f9aec41ea9fa52334b246d476203f7c2d5f6575b7d126f845c8396208049ac014e067a49c7da865ae3e17c4b4630e63630abc4958e1924a21d32c26166f01ae2b8da72d7a56e1afa6621287663eb5d2983187964f81753ae5213daad6800140e34f3af2d7e227cd2c5bd9d4758586f6a4d60cd0cc70034d64e1fb4d2ab6ed9be9b2810571f2189149d9dfd08f1f8abe05475d3f445230413f3c2d8d7a32ff4c27a59f3922a9523e81ceb9a2b0a35482808b08de05f2686e7dd5fd0b5cf19516cb0558a76d4d97770e28404a2e78dcb63f3b96eb19f1bbf291e88a6145eb27e6fa5ed3609e829094f9cf937c58dd2bbeac045e27afa19407b425abe489f434d671692197d5cf72c07c8e2ac447b2db81b8e0e248d621f0521aeedf472184eff3527a64c13f0c601b3ec2048af2672255c14010fb1430fdd8b36f426c6469ce3c6b0be2cac2ae4ceea8baadd17bd0900a75114a14a370e499163de5a0e817ec703ec5b636140892fa8203d20c032cbd574fc34d2385320f8ef889048179132035bf18bc196f5b08bc5e4d0ae4bf18768342d5b48f90d27c5eda7c200bb025a2de774ef9a8dfd0d75fd844969950a13f5b4320a250d4a124fd0beae2313ac372d0583b79543c07fe4a31afc144acf99238c31b3e0b9c6241225467d7ef6a633313309fa84669b737cda0632d2dd36ec901e1ae3e4a68210fa0b0fa02588eff6e72f73f015951e42f85868c622a8be41f3247a2140f3fc0a30d31f2379fd387240e5f306a17b035720f57dba876e1cd0d33bc87d966d620e65736370fc817ac4ff15db12147cc85c230ebef736047408e03ca8f0be6a642030b5627458aa1e65dd56737ab810b2a8b492586c42e193ce28d9aae97a13db0cc36d358f6efdfc379ee394140a6700fd99e86b8f7d3a594cceb4bbefff68ce15866cdfc9894097f08e89b66f8c30bffcc001044b0594922e11b8a055c9f6ee026ae31ac020370a1f3e06daa5a772272941a101c11714fe8d8e37d40c3122c61d8f610d194cf82da8419cfa1680eb14ee96b6686e9f209a00df36e96a9d6f842571b23c753c5577e9ed466bb585e8910722050a93a8463bb655ccac5d9900a31a460738ff88a624304a1e5b37fb9ffb23887000c006db10e2b4b8985770d88102ff55d36e7513f37acb4c5920fbd220fc20c8b9b1a83128b0c64bcf26f761bc01407ff23a9b11942b6034278ae1b4817169180989eef86eba3f781c6ed7c82b279089823170533c0791d57ca17f1beb9bd6fda0242993bf5be154fa2377340d0c0c8fcd0da22a25000793deaaf634421431ac4d940c6ccbe9265f52eafaa6660cffe9ce6f0ae13c38cb7ce5304e697ff3814a992c030ff909d9b33615c8f9be2f199990be4b96c115324f1f53c609d9c70c9a3d643973be4f407aa54cfcb7741124c48570a6a3906b2eaa017d1c4e98b1336932976e7ac4b2cc3ff548a3d5272ca9b66092c0be62c903cc5f6022fe892c37fbaaa5bba75f41e40b410b223b052ed39b580fcc936b9582cf7c479ce148f56a374df66ddb07dd6547ed67da09e72e7820676bd33cc3dcc778b5e5929677908c6f43d3f2e793b768cb2bb6c11cac258438c7630aa10ec88ac11e66c0855e0be63beebbc4a846a4210d3b16a1a45b07144aa8b8587d6d981683640bcdb7390599cc6b806869d01e425cc59f78d26e009412daa789b0872610de5d576f928321c80f83d64dd5cac4562cfbd72b56bd17165c7c60feb78c973289e4afbb58225262f916b2e189f9195aaaaef30f037d1a0b47a5e1ad425f16c581f4317b4a1cce0458eb64e01a2ce88067b6365f20570e7a6ef374c26e7df9d9b86f1a2ed3f3a6f151d108f24245346f7fbacc41e39d1b89bbc83ce93442bd733e4fb70c090135e71518d07f6f7cb5484256d973b5e107cadc46b3caecb4a0c85930d59631ae69c164b9b51d4af12b414da442ca6e2e2e905b3d35c848bc74803d93cdd1a52642f101ecb76adfbb324aab602cda06f4142f88ea187aa2d9bc2770fb55738bf1c22a0172dd3aaaf0756da7919ea8fd480bd1852cd97487ce18b5406f1e208103de94a56189a902b0e677d3ebb09b9b4403e78dbd0aa42f5e177f3c0e44bff9304af9cfc15d61d2b7172aaff7db8e31c401014a1d5637a540050a7c24f8ce92509be63c99f33c3e169be4e48c5c46c0d00536d73fdfaacd543fc05f62e194e64df3916521560c2e7c70651b0c8cd1711e13cf10ed6d7db7642d832dac4b6699b200a8bae01e5c271a7cb1dd55be7593252ec16d138fea76b7ab76eb2597f4847ffa61f1e5560f85d5b1dfb976384bd384137d0d599a996035b696364b2bbd910add9dd520e1d50d4acbaa28865a9eceb70caf6cfd4e46b787fcef171be5316b41061ad0f844d78c571948c0c3b9ca5b581e66da9006818668cb3024873fb6c209e388964e71e4e7481f60e98293614a491c4343db492b43eaf5acc1b24720b645f73a741b8082ec8e7f0940270bd337222e0e5f38ae454a0bbbb97e2684eb4c0f8665e2f5fb567a7fc8f46d42d76a87b9167d5caaa34cac8c6c35e92f339daa280b074923950ca4fbe26d12fc08df611b122b294ee8165d619809c84a8483ac031a91a321ba578cfff08ff1da3638da881a658ee5025ca8cd4c2412bd64fec5acce4a063c903a928a1bbe47a19d6f20ce2a45e48b775e956e7328e2807bdf0699717624dcc4bea3f49696076d6bd1f3b32648c84b627afc98ee7a255a9b6a7688bc2cb1d022b456c914d73c31a61cdf1f0990d46e50809e625fee7f66f13f3fd3d711053420474e2137299fb499c55d280fadb264bfa1c01fb103c85e1025144416fb45c410b1de94ce3b6b39e31dbdd40085c18596d5a4f3aa5d72f5d762f32657466c3d6cfa119e4b15d66c166f9235130b6b5db7bdeb688674c2c9ffe5953f913dc8b70c89ee3a3e9d0b900d3eb11b0c998e9028b8af68a3a123f0a5aa56f987cb287bed1b4ec7ea4d5080f7460191140fbbc521c4b4ca284d2f52ba94d12c57cffdf6b3a1823c8bec3f227f0964d0269567969b2a6952d67b2cf3fe4ed64ad32021de8df302d6fa0e5934dcb0fe0207c4eaf81924185e0ae50fa740e900c098f17483a8c8e9acbd142b0d3b8d7ed31222b7d9d9642133a4d710850b29be1b312f2043e0553468a03a468da16119a0147bc6655daaeeaccef77df33300f0304b229d0a67f2e2686ed0faf697a8c0be02a92c5eecec61f86730c46d3e4060cedea01e6ad69edab787224daf217fc00d25bc51eaadc89bea9ed8b8b064d00a603bba923afd50feeaa28480fe0bf5208e1f6c3c1ab06c7581870abe1d4263967bca1c83e6d829d9967b15b03c71662b051fef32423b6f60529ef22b34e79fd39a03188920fb378c832b94ae04ee9c1e611c19e994f3fa9a123454d8453ff1ab96e3dfaf32adcce9ce3a58d3ab46800283017c41df169d9030faa8635070293a6b4f92c2a03a23f322f9219eea5c13a65e16f8cfbb4c12898a9b5666c005ee1bfaa0d64396e1ffb0c89394ac75ce404e341592c70ec8ef696ac200a491dc290b060b0d8ab0b0d73a785117cb992fd5082920edf8a802617e40021efd904982120cb763677cfbf5fb26e040250ff71a9e8f0a74f8c8c0cbb2d83b70592dcad430759e5e3365da322b45198c34067f1ef09108461a7aff586e522eff5dd06ba39f32adbbb2ac11cb5ed218b080ef6b47ab0a21d7658d2c57a0b782547f8c5acce7c340cbc4e32291a4f4d261d51eb5d19fc313f13da04d155cee22bdda2a05328dfad96d058d2752e41372178a8ac3069d27277f5ad615312c8a0da2d6f5967a6191fdfa66a465ba370e3d8a8b27f31b75cc1d1dfc9e914df09e8044c0d753950f5a662bdec0dc6b50c3fac197df947b2fe927b02824b638eba85cc6b3a3a4633ea595e9e7c01764122a2a188c7f05b86a0901781fe9cf000e194128b474d8334de7204e7cf9ba4c45a82851cc873cb2d851257d3c067a363b22d3ffc4592989e1cdd50333e7d20858eaded8fa02b85209c329cb4ba3f99922719ac16e761fee300db4c4022070de18149af7d30df3be006208f5703c677000acfdbdab00aa099e0c515cdd6c36e4c2fcb14d4a169ac5226206d780418161648be96d949099a7cd18b491d72cb00baf6a36552c49578cb858004493fa5ffabec5316a34f8af4e3e329c28ace755c8329add179dd15a716fc30cae70fd816fb9ae67f0e595a39585efe683dac6569cecd5b4612600ca7e58142774ac04ad50ebf3fd3ffbe32cfdacc60c4942daa5b1dbd52cb4570ce3acffe92365dd10cb6526daa7ba1a0b8635167f7220c97ce29b11fff0e2780c3d43d82700b6511664384bafbc8ace53cb17529a7cbe4fa1e5b8242a149af5120092d5f50af044e091387602eb00690a21ed41b2b6cd2ea58ffd23c273f07932cea2d0e81664d0b1770c1a8726cbf09e109bb62cf0bbd05bd068eeb95e2f120470b4082e0f037bed194d5aaedd71fdb36b71ebabfc29ba3fc10086cf780c1cdc74ebe0250fa517cbde50830063cefcc39e7848ae2f4ca899903296fefa908e8c18e1bb8a1437ac612ce96cf6c0617a6cd6c6e8a631315a1d3e29d803b558c3a4274960d005b212c9c31e65649812c0dc066ae224f5001a4893fdad31cf49271b8df0353c2feb0c6796198d5ddff04951416833ce4200813f0f39d2ce75da2985d4329c1f2b2023d34a62804670f522d449b1edbf12c255673077c821683b1d8d7e5ddf800a4b7b539cba5dcaf1fdb336daec57ba5cc8e484c04b5e70f74ee3a0a2e954e2175117e97ac2c5bdc2dd375946a0355561c2cb8edb457d5185a93189e13a4aea066c51edf64ec0704d127db99b2ce23ca840435bc4ce9d8b533e23bd09c80f5004b23d8666c77a078368ad040c4e1ce3d4fddf9ed1b1863c9ed1d2eea60ed85f24edbcea0b513edb75f6321e428db3b3082154a7923b2776b96f9276f251b155188c9415d7e4f09e37f89fbaddef8410979efa36c6a1efe5565349a96498aea518ac498adad8a301e7d171c35883c89798ca890032253d77b00393525dfc1c992b73dcd9fdf3f304f8b5c1f7a71f4036d4095ffca43ba1c2ed25761063f0ee3925fcb36659ca28e692b6f5e89599405f990c57ec7237311d663bdafde7d2f81d0dcc1c522e2497eb105f9d9bb4f969cb1c89c2c4010dead0a19023f4bf15879c2f13afbd1caf80f6e3b5628c1e36f57189ec2f5a61d03eaa58e379df3027da6f24403ef8d186c62c5176573cdb2e799c65bb6c0da4fcaba21d4b8c02afeb6b8717fe4b74f45e45025aaacd2ee55901d81ab63d04853f222695e0969e3a9f89b9297aa44364a7a2bcd0be5759f3a27bc00d7a99f32b69cce48cbba3cc858a9fc708deb0b9515755e48edb793a598de521bfecb827ca3fe1e58b9875426b76a268137ba4e5fe24a5d7f977fe9d1b50ce573fe98be883ce057ff046e26d999403f52ff08e8a7dfb7f993a389212ea2ed1bf7725f4a265bdbfc208fcda3df33465c90928f1e1bb77db1955ef73583031893947c902eecff3fe24903319d5c2433e0b0546305540bd96a71fda765fcad36af678a4f07cc95fcd02e81ac2760931d40c132c998a8c0b120abd2d3e666518d1ef5ffa130cca13b78e57cad7a151bb594a1e394e73d71aea3c2d6e5cbbff872c98714fb5fb571e1e3743eea704b295666d21ce4e84ca11a1d673a27b169c5f83088e0ed344fd75dd4b741b05226ab93513305f4a7f83ded532c62d697e04ba2a963e30c321f916c0080e9fbc5ab619aa8c220f38de69ea6d5a49d99f904ace4f9c6132a8708e225fa2ab197c4508ff25c42a92d2a7cc3855cd59f6c96407266922cfb6f523d46e47e29d0447f32582e1951a6b0361e26226f8379a50f2d5e3204c6828da40b8e7cdc816f6b166ef4aa723077f7be16b732f1edab20a5668e50844b717dacc74dd94451cb9460cca88ad8a22ff47083c488ffc04e3b9c39ec7b25f41ee2a2a6cae755bc5d1fb9e1633549013968d569bb6c6f0d95547d93eba03a1da40766f3ab3b18f19b30964af1d69db15b13c2024eb855dd70af579f46fad36a77a77c2cf65812e92125f2f9e4011eb2ccf4716ddca655668da9838e29677662101824afbe875bd3490fa1a7a286ae31704b40587eec531af8bf89a26692686387927daec1d6e004fe9105160219a7d23035a362fa532f57b6269292058de35d8cd36c4c6b1143821adbe16379d11e1232646f4d497651f0954ea9a1df894dfc491003980f60b52cc0a1ab26756077227ee3102ea57dce3271965fbc8bd6e66a68a2b9543b577278024cd4536b2741226c3b1d409828f4e0d9874ecd7069da3c9c77f290915adcdf6b456983796b3fc28042c760483b93e07a9ac85a3991713d1c2f85b94556c6d1a273da1ee90265d01a8981245c7f7f8d10b2915126d9f9ac61a2d6a8c055ea87be9bafcb5643c9b119120d465b5ddb005f7ccba998fd30058ffa33d82c9d8e4e301e780bd7f93832604cfea9de23f1d6b2a50f65047ac9f879eff0451f311164941d680b76395aa208016ec3c5dfd1e4e2451ec13fb58f2242b1c26e54bbd88b44e163e4e7c7aad0b3d5897b5f0d8d49522bd7b5c1aeabc1faa5f92051b7c9609bf3dabb085a533130bb3f740556f8e71453758b79613a124d615ab01b2e1de8a4e519947fcd62b059aca90452fd3b086e6f37ec6b9c8c565885d7bc2125f2a6edc128992c986ed1e16fd93e88a03407f497ac742f49836ac0886ff6354b8a0d7096dd464cbbd9e1182fbc13662c46028cf859e93de493b32a8060828596b2fc4245655a4f9589c0c3e6a3130d7703084bca793e15a9e9d6366f1898695e5d47b7da2fe660461450c47a7186e240f9f600739c2fa2038f28aa34075563b42e39189fff917f77b1d169abf6085508100e18e9651f68113f0371c993ba004d5ac582763655c4df79c28668406aa60c8966a70355446b5ef97eca8f9c21cd311efbd65d6701932662d214ba804cb02a5d0c4e936c50435a6d8a2678558c26aedd38694ee726b9fd89a039a0378b17166714bb1379bb173d2e916411278d8285e9e6dc901a616904b1927280ff562b631da5d3cc81ff70e3269a358e8c4dca3696ccdb546ae1147014621b472511388d61cb1e60590ad87f05617f122417a4282d94bbc643f0547a6682008695507d53505be0bd9b78ed3c7adb01360df7cc3b4e8ac5357485facb49c2e0600e9a923c71d12fcaffcddc8de640403018a9a2ff1c2ebcdfd6700facbbb2dbc42db37fa4e0b64fa45559ed421cf9e91bbb8533983393113a75023ebc8090bb50f8d0b75903c3c8e4b51137f3ccf2b5ee42e549fa6c2c57a336718e1867420e656c75e985702a0b1d78e451a4d0e7abfe681ecfd92e1fe5d00c00a22acdd0ff415858ee7fa9d195fbd1071d672106427bcd2c8f5114e6b4e3b8f6fba28c62b38318cbd8088a2b956ddcdebbcfe0e6670d37b63276fa30a16791a3fee1b9d", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } }, diff --git a/circuits/benchmarks/results_insecure_agg/integration_summary.json b/circuits/benchmarks/results_insecure_agg/integration_summary.json index 9de4f9ef0..005a4e2c6 100644 --- a/circuits/benchmarks/results_insecure_agg/integration_summary.json +++ b/circuits/benchmarks/results_insecure_agg/integration_summary.json @@ -24,150 +24,150 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.124654569, + "avg_seconds": 0.004296347, "runs": 3, - "total_seconds": 0.373963707 + "total_seconds": 0.012889042 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.625217639, + "avg_seconds": 0.020525153, "runs": 3, - "total_seconds": 1.875652917 + "total_seconds": 0.061575459 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.556127167, + "avg_seconds": 0.019100917, "runs": 1, - "total_seconds": 0.556127167 + "total_seconds": 0.019100917 }, { "name": "GenEsiSss", - "avg_seconds": 0.166924514, + "avg_seconds": 0.006925194, "runs": 3, - "total_seconds": 0.500773542 + "total_seconds": 0.020775584 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.241305361, + "avg_seconds": 0.010452847, "runs": 3, - "total_seconds": 0.723916083 + "total_seconds": 0.031358542 }, { "name": "NodeDkgFold/c2ab_fold", - "avg_seconds": 7.62245093, + "avg_seconds": 8.270900388, "runs": 3, - "total_seconds": 22.867352791 + "total_seconds": 24.812701166 }, { "name": "NodeDkgFold/c3a_fold", - "avg_seconds": 35.477445166, + "avg_seconds": 34.927441903, "runs": 3, - "total_seconds": 106.432335499 + "total_seconds": 104.782325709 }, { "name": "NodeDkgFold/c3ab_fold", - "avg_seconds": 7.549333777, + "avg_seconds": 7.542320708, "runs": 3, - "total_seconds": 22.648001333 + "total_seconds": 22.626962125 }, { "name": "NodeDkgFold/c3b_fold", - "avg_seconds": 35.646490666, + "avg_seconds": 34.922820125, "runs": 3, - "total_seconds": 106.939472 + "total_seconds": 104.768460375 }, { "name": "NodeDkgFold/c4ab_fold", - "avg_seconds": 7.196253763, + "avg_seconds": 7.895315874, "runs": 3, - "total_seconds": 21.588761291 + "total_seconds": 23.685947624 }, { "name": "NodeDkgFold/node_fold", - "avg_seconds": 17.672638027, + "avg_seconds": 18.310982819, "runs": 3, - "total_seconds": 53.017914083 + "total_seconds": 54.932948458 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.409093792, + "avg_seconds": 1.566955375, "runs": 1, - "total_seconds": 8.409093792 + "total_seconds": 1.566955375 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 49.418440542, + "avg_seconds": 47.874825458, "runs": 1, - "total_seconds": 49.418440542 + "total_seconds": 47.874825458 }, { "name": "ZkDkgAggregation", - "avg_seconds": 20.366319709, + "avg_seconds": 19.861393583, "runs": 1, - "total_seconds": 20.366319709 + "total_seconds": 19.861393583 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 3.170409645, + "avg_seconds": 1.358248069, "runs": 6, - "total_seconds": 19.022457874 + "total_seconds": 8.149488419 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 111.173713722, + "avg_seconds": 111.87172225, "runs": 3, - "total_seconds": 333.521141167 + "total_seconds": 335.61516675 }, { "name": "ZkPkAggregation", - "avg_seconds": 4.325023125, + "avg_seconds": 0.925223417, "runs": 1, - "total_seconds": 4.325023125 + "total_seconds": 0.925223417 }, { "name": "ZkPkBfv", - "avg_seconds": 0.433745833, + "avg_seconds": 0.218732388, "runs": 3, - "total_seconds": 1.3012375 + "total_seconds": 0.656197166 }, { "name": "ZkPkGeneration", - "avg_seconds": 5.172915375, + "avg_seconds": 3.515460263, "runs": 3, - "total_seconds": 15.518746125 + "total_seconds": 10.54638079 }, { "name": "ZkShareComputation", - "avg_seconds": 7.412439382, + "avg_seconds": 2.332301763, "runs": 6, - "total_seconds": 44.474636292 + "total_seconds": 13.993810582 }, { "name": "ZkShareEncryption", - "avg_seconds": 7.468529083, + "avg_seconds": 3.965180039, "runs": 24, - "total_seconds": 179.244698 + "total_seconds": 95.164320958 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 7.677126833, + "avg_seconds": 3.440710889, "runs": 3, - "total_seconds": 23.031380501 + "total_seconds": 10.322132667 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.114853486, + "avg_seconds": 0.103301764, "runs": 3, - "total_seconds": 0.344560458 + "total_seconds": 0.309905292 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.24184635, + "avg_seconds": 0.285312216, "runs": 5, - "total_seconds": 1.20923175 + "total_seconds": 1.426561084 } ], - "operation_timings_total_seconds": 1037.711237248, + "operation_timings_total_seconds": 882.167406542, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { @@ -177,68 +177,68 @@ }, { "label": "Setup completed", - "seconds": 3.042068, + "seconds": 2.7038335, "metric": "wall_clock" }, { "label": "Committee Setup Completed", - "seconds": 20.254953375, + "seconds": 20.174264042, "metric": "wall_clock" }, { "label": "Committee Finalization Complete", - "seconds": 0.005661959, + "seconds": 0.003923875, "metric": "wall_clock" }, { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 131.583918, + "seconds": 131.619168, "metric": "wall_clock" }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 159.570454958, + "seconds": 144.272573833, "metric": "wall_clock" }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 162.126205083, + "seconds": 144.78216475, "metric": "wall_clock" }, { "label": "Application CT Gen", - "seconds": 0.3074025, + "seconds": 0.009741125, "metric": "wall_clock" }, { "label": "Running FHE Application", - "seconds": 0.003480958, + "seconds": 0.000051333, "metric": "wall_clock" }, { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 57.874201, + "seconds": 49.457601, "metric": "wall_clock" }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 68.103220875, + "seconds": 53.35583, "metric": "wall_clock" }, { "label": "Entire Test", - "seconds": 253.849896667, + "seconds": 221.027911708, "metric": "wall_clock" } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000ca0bfc9448e9ef16c000000000000000000000000000000000000000000000002c6fc73ad221ed124000000000000000000000000000000000000000000000008224c7cdccbb18af40000000000000000000000000000000000000000000000000001877d7b0fe868000000000000000000000000000000000000000000000004ffaa9844e438d4d300000000000000000000000000000000000000000000000ac1d3924637ac840700000000000000000000000000000000000000000000000b23147b92159485ad0000000000000000000000000000000000000000000000000002e033f6bc040300000000000000000000000000000000000000000000000768068eda5eb7ec45000000000000000000000000000000000000000000000006b320ac7697a7d933000000000000000000000000000000000000000000000003ca3c43312f4a1a730000000000000000000000000000000000000000000000000000b90e9ebd4813000000000000000000000000000000000000000000000002d7efb8bc1720f5e4000000000000000000000000000000000000000000000003f44e6bbb5f7dd5e000000000000000000000000000000000000000000000000d73b5021fc0617d170000000000000000000000000000000000000000000000000002138f4eea500814ddc04dff7aade6906eaa66607c4f4fc4b7fad8d7d6dc1781b1ccb5824ae1460e35805f5cf672d03d0c6e86292f9270e308666e0a68e54ba34c48c7d1c1171e08ab78f3b3a380edceaf2be1332bb0f6970b24f889216fb52b96903c3672078e2a2d8c18c4b84f1ee541265621917acd4fce05eec99dc817ef9a1b30df545bdf2ffe8839de28e724ff17364a02a9190034ee4093d0771fe513866af5eee3e16c2640fd2eed50030bcc39cdfc2089b3af2b2fc2cf47c558c544d264cb316924fb06cabc1c64e480876c03a96dd26e2ec80e2a1fe4a060eba855017ac571e27bf226f2d314aa75307227826f8c8cc3558237c0c85427b4053190b5a66b8518499a04c8896e47bde11d94d474f7eb7e24a697ff050a323e51605c0a2f3795b43a55216ecd8b7dbdd337ec0eed71fac7dca88403ce3aad06ca8e1f5804dbf2dbfeea2d01dd0ea69cbd98b44cd4163166c47696b0cb64815f84169ca91ff184bc2aa703873167206ba8215672551c6c39711972e0fcb6171286004706f3f61d7c56d50f286c3eb1632be8786c44dc777090a9f3b2528a91259674a958b8287d9e6e7902eda561c95e35960e5fc1a918c47214a8a3fd83999188e9cf8c4bb3af86a867072be6dc0e4cf33e535bb85259a027f564bfcb50f2633630222848a6fbc30bc81f15de7cce451611df563628ebf63c866506617a24c47ee9943936bedf6006d403e517ea950c046b99c76b6a2f049de41634d85d23ec7fe432a42c24131a81e41e87bc342ff0f4a94a49a0e42bc24fab0ad8569dbe45de50452d2f1518f6964d106282a56209e9549419539fa23a7a21d5adb558af735bb87d547053b4b077b328e1943bbf60c5f59f31a56dbd1bf144efa369f33bb0c50f7e45ebc416e850d2241b4c910649052f98c615c093e763eb742f1cd429f36c4824b3f875f18c45581825b2a7c498228972f28cc96b5c90ee1c1dbbbc46d9ffae4973bccf711fca6d17a07d52ec5c6aed0fc3e9a064c37ade09de5dcae4e637f46271f943e8933c50061892e9ad08e8bff03cd7eddfc9ffada674bb27d9867e628d90ef6e9fd19ee92222e6a3072e0948ff4d178389a49a145e68efdbf306e35874e3ee27452ff17028fbcba83976dfd49e75c8bad82fb9339e77aa7fb76af7670b90e084210ebcc223d988bb703c4a113f7131e52a583d944668b72c08fcc4f552a6927dc3c7333b06b41b4b298fb5caa9170db380fe81195d0da8c38d6de7c337c52e5f024f046526d3b8bf4befc00cc172fde6e835ebe761c7af500c1a3de202df707e91201f7a02d38fa8b9456f6777eb006c1834afae72b3e500ffcea21a36b82e83a704da1e1863aaf8b58a8662f0dcb6fe6fd3e35bcb4bb922291315bcdd7d2f00ccc7db3d20e4d6112826184c37f83fef8bbe495aa3976e2e8c334b967ce4fc0a94d26503189d92a2f7b89fea1bcef0c5ae1147d833e4ac1d52120b36cb7d10619527140b27a521e78a3d4075e127c21300306256edc76d094502551553efcead0caec3b1097621f0a820ca04263bd57e7afa49cfb92a50eeffe2ac26f7502a496adfdf8e23d183479522e4fd838ee09a8fd50fec4a22103536fb118a251999b16042603d025ddd4670df746903205ec04aaa1c131a8565298996d2ef8ae601c995ee0e1c27b2ee13e4555206067c20ee5e054c793933fad112f9cfb4edefa8b6a5cccc8a01fdf4cb41e6d9bf4216246445428daa66e8263a65b79139ab1011ef9a150b9b1a40b8798c1e4e008ec02795bbf95bdd7a531a94ae8df852e92b98e7766fa3ad0c79d387b21791f763ca553d7d225632bcf58e60c6c608ee1443f98f979f9d3603afa7aef8fb5c103ca78c01bcc6917d4f1e0aa09bf3ea417c51c1c3f9ea2b902151931b21c9d25e574019fb092e03c40399b0b8fad6e3347f5961674c366450049609cb8eae6ff4e987806a36851ec132675dfdd390f61fc185b808b31af6ab220359a2710339887cdb032a26cee0dc574e33c1651f7e8b746d016f39fff2c605cb5e7f0b7a839277f904d2bf506ecc02f74be5baa88793d3265700bf8801312174144bf5372435bebb6c3af3d31bd15519858190765d99eb03caa1b516aa3a219db366834e6cf2d8dd47bafc50fce17f23a9556dc30699f727f09edecee94d252a7c4d5498c27a3915b7ac677625ac6829b5777a2dd234491d87168bccbf5301b9221ff82f1648b3b24d2e107329c0a910034296426f76ccc9941c1267df6d14d180edf7ff58f8aafc9fddc2b6a9a641e608c5d6ee03ccd615b645b17775502ac1b479e0010f878c96dcfb913639debcc8dc163faa63994d6fe4299db056f125cbabf7fd62bc792e6505b14e5849ef4506bb43babd4cd46e9df6d402c7e4de2e213bf9ca80aaca06b1baeb5554cce579df7cfad676200cfd2518592c92c6810728dcbf5a308018bdd6a9d0d6a3249cd59a8725da15de0a96a899f510c5407711b0753955e733c32aa3b780c292dcc4a8a24ff2f7bb224370ad0b0c6fca1f601947e99c3cbb54eefe69676eb12aaa3473410966221a4980511010e6f3212c281aae2b27f42ea11fb627fa4c854828ae3d1bb27551fb5ec75b54e342f5cef48f0d592aca51491f88b890e7dd0aaeeb290d165a763aa3b7f3b34ff65fa5a05f8f014f6b3daab5bcefdc03d0aa1f38464f5b0791d8e1f379669b72f548d9afe3490cbeeb0626f19b784265812c1f6161978d27f9e9c0349f028fe3b71197ef130c2dcf2eb7d5edb0b18a4194616eb35c7d614e26fcab690613d9757b323d94867d0da5034a534f65584cce55bd9c5119c20d2b8431ace83236e90ccb7be0c387b11b3c916229a3bd8603bfcb9e293b7c9a2363b898382d57eed13d5ed795066b07218dab59a3ffb999172a0025c43d200d12d8a155128c4db831c24560f654e43f1a27266db9ad1d94700efab02e2f0d39c6e2a84f02523fb12d290ba50e9a1a9805b38dca8bc7363b838f11fc5fcc4a39acc6c72112807ce045be538f43e9f27c01e75d05c14f7596940ec764feb95e2e28f10390a0e74e5cb3f16140127cfa6a156fa560a3c6e13592c8cb5acce08db1c9874a24e8a9ec69e453fa9f1384d46b1f263787814072fc03ea72b8abc134a205c058e5e9b963cf4b4b043993a01abe01f4245be8b9d7f9f127e5c248ac6c5b5dd7b3454e07ded8374360d40d75a39d1d609f99c2bcec53998e407d157b88698bf2b4c830874dfa537b5660303e798d095fe35d6c3ccbbbd131305128dfc78d99bd02faaca0f03250b0ba2cf8f859000afa115aacb761780b4ccf0eabb203038ea0ba410b2444e91ae6a10d0a729763234c408b6cbaf328d74d6be6bfdd4b4bdb74bde0e49da02911bbac860f7b1ecd27981f9ba1b18d35d4db16012579d470cba0e5e117421ac0da980fb45ea23f9b1f4bfded7640d9a41767a3819ffed3d0b1ccff98ad78f63cb4548b6c487e3fa90a8e68436d8db97480afb645de0e76939f6aec3a9183ccc113ae639c4f445b4c161e47279af3abe35b80d6a971ae876e0bcdf15ddc455da007ed56c493de38182b07e1f76681a2fa9196f45c3d25f98606688b55ea6336e19638711ff00d788f16bab3baf7545061ef03f59fd3b3c69a5da63683b801868689aa56c890cba7640b3ef1806aae00be03157081b188c4bfcc045ddd65d1364bbd2f73d03d5eccff18cbfd02fef55f8259a3abb1cf3c80dae34193e155ce2d02117b2281181acad525abfcef8f617e08f3264d0cc3676b330037bef0c76d2be781d9109130dae3a2064dde40ea9103327dc858625e5c5f43636aeadfbbf08b766607aea64758115d12f3a3ccaa680c8bac7c90675b26db2aa4e437820f8843f613f9f1bfd8cb4a431bdab7ae3f2815251e6440f4575397b1362413dc0d908ac8035d7e0dc154462102269ce752caf1c0e965c80463937edade5f71dbb16559824a082fd53c65483e06fb749bec6adfe9659772b8db6d26541bf074d751096ad81e99c52d7ef4102c2646c0a220aa8b25cc7fff384b029e841c5e1d1a9663e247c9fb35838a44322b00b86c4f53843909921594d8772dd67f6fcd57b555453ec0e6a2914fb82b4f9e2fa795c99591a02f6cddcbeeff3995a26eff6d038b7c063ce6b7e95f591130710c9a83eb1d4092d30b6e3a0db2643d049eddbb6486ca3ff613045343ae99a6af052b868c84142535fcf42cd31d9c039d599e1492cce99d4dd8439330b0a2077f165fc7e1fb0a27e24eac75faefb6ebb8b825e535423f5663b09f7b157205c0302151f296af0b1d5734e1e2a8eb3cd397a8f7e9cd015e333ce73c33df4bd49e860bda526af203f82dc6b615c2ebbbaa3b99a13e45f231c133bdc755807b03311123d395bfe1a13422c38063cd2f8cc6604a3ffbad19e5bbd5776668f5b907c3f72150da121fc376b7231173de23fb4b6d2edc5989dd2e82ae695cce929259aac229968d3d266b9913cf2d577474ccbda946ddc4ad52cab0388806f364246982402ca8b651f56a0f593424f4511a776152de94b2d5e040f23eb6f50d03029a0731056d6bb00954510884059b04e580005f131b335f45dd627242e573447d24ca9317ebaa00cee1d6cd54e10693d4656d19191ea9d63f1fb58989b317a7bb4f173f02154a9ad7593c3f48daa36a36deaf60a363365cadefcb3cd7f7eedf78e07817168d8e60ae9532d27b0eb422367876aa2840738a06d8487727fb74a6e75e44f6045b19a874d8bbd97efbaf376dd343648da57e8da19a975e3c715af62bcd12cf1b40e16223afd640866cefb3d7dba53e2e6a306634692d558fdf6ca8190683c8241d67526488d47450901eee756a9ce807cf18f54e62cc823f5d47b4e3af46ca1f3b94e88bd4115fd91451db59a8e8dc0c111024a2b38af1deb3854d2994887b1037817024ab3081acfeff6b50282ba402d85b56966899fb39a6c01df6771f33044b41a29308e89a75b06b6627d85cb028d191f46bfc52df2723360f52662e99087a5737c61ee99f7cd057bf2e18689f5b747595ff1199ae6798b0499c8d75e113362ac3d589042b46807e6fbf16b6e40c8f94e444d7652dcc44d8984ad500cf1d29a2242bd30bfb5bd3eb76e7cf673275c21f9ecd128d346760169d28ab130b042b4111792bb700688ed4c313c5f168776bebd4efc7b5c8a244d8ba035bc42d039278015f319ef204e7869582a42a617cc51566356ba50ed425e8a4b749d81a0ad6f18abb1f53267cff6f65e6714c4a397efb80184e037c6327175f09bcaf7e28e8c9aee1ff608389deb8627134d0ce9fa5feb55c170e782d5214ccd605eec1257df978c84c72adec12e2b0f3126371d1ee637532aa455e2d2a78d62afdd7180045f5626941bdf1f7849adbcb24000d1c9eac6d91adadf387706d72b5dd34e804d4b52193f0dcd0d219a9063743deb5f4349336dbe6b49dbf416c1789d3d07d304e7354d6a54ead2ec2ba06b67b345473b6e1a9b1c26156c39facaf76f899e428ccb16ec1924cb31fb117f22bd7b131cbd6ea732abfc50459469e76d07dd90f1387549c3b417078677264bf1f284a099584aac71c07e7fb7363c60c868f435d20a32b528b59ce742f5afae859176c4931b55e6c0fd7e3b34fac1e1fc4116fb61da0d8cf4cb32d83588556adfc358e1130ce98576c3a350612bb36da466e5e430cd2ee1202e4ba044375f6bb5082a688a43cbc32fa463585c9f90a8572910d9a06c16854bbd5268dc754d94d49f89542e8cc8b2ef418ceb21cac5bd74938c7fa1da3952a91f4563cfccc6eb1946a3ddf30c389085a4ab95a232b732e139e86200d1a9770453f3cf095f35f9ddaea29d99de184b8123a5f2b63c0e824c16207991ad29daaa0ee898629c92a646495916aa6fd424d5015b85432cee703ad1b2aeb038313c5e1410d79237e6ca180f40c19d5e5b2513a1377ca2fac6715217ba9351fc5b3942d314b2e93c71a2f96d9155db47b6d78e1cc2bd004aa22003e163b6412a752ca2e7889cefa2751358fd0fbe3884d99f106d68c973c1dce717a0f33eb02837031a40957b5fa4835b44747471801e52586ac30295db9fc1bb1a704f18b0d1447924bf9f1e02784cc1b0532824a4c078107acd6fd9db620fd1a61a0b0ee0aaa2950bab91529b7454b666079592dc6e88fae0ed90f90d35892225d47a4862f8963dea3b555a8f01f4da08bd46ae1ae6bc16bcf7005347c8c19de6c2145ad125aa502df4cd04faff782bc862e046cc3240bf2c8bcd04f70bb4ef6d36fa27c1677f6bd1cd6b98983ddf5012b2f031398fbf6ca5c8a32da32cde2e62c75369011f86008c2d4c0ca000b7ad4499d797d6b91fa77825fb44045e75a9216249177301422c032ca94ed2749e37ac341bfcd3d25fc7ecd745e66051a06188f3f4a0b07383c49c3ef429d01566aa613cf191134cf3431342729cfbaa7732cf5482fd72aa56ab908372599856c78c0b74632d5d60966806bd67d0f82f96c58df6256021c260bcdf988a10981f18558f30407b89644a7ea76d78c0ddb151d55530b667c1343edabed74cd778cefe84306c1eab5bfebc5d97b7791300950279dc2aeae2f235b102f73827cdd964704d11d5240f22d7da11fa6afa07b841291a2c1a7ac492c757990536725bda41c4083e8acb511abb6eddeecf6e50539c4c011b3d1d10a27f1b8b43e24e1a85a932f807aae6be08145cb67c932c8a20452d5cdec07bb06169254ee048b3edeadb4c9934efcf8527debbf850f8903614da37e81f52853002fd4cfb00556968925d6b81c10197dae197b9ccfece01494971e15967e157e662a022c3517eba2f66934d09cf0610075120f622acdcec3064a52150f6d095a2724e0d64d5824673d815ea1dec20dbb5b193705c78f2b4415be9e5c2ce67e2d1d0846cb6a25cf5de0808ee27abc17f5508b07f104a3f3f0522b6b226f4b7b8b8e0e999218e897f2a6d36fb1f2dbb1e4b57ce685332a56be535c6c6cc5d257e9ff05a455f3cd0d1f8ea76967807a251b0bcb3a1cd8da51868169fc2f727c528545242850473b17de1e572da6cc3796b98847e836d80bf2ec2618287d37d544866004e27ac002f00d5ff8399f2f6c541c3afde128584ec229ff1bfd92b8fb2fedc826a12118800da2ff24c92d5f7d15f409cc23b8cccef62cade5ec8af6270aaac10e4c6b05214f6fb1f74325f1ea5d67238b2789d21a634c24e687a7636b7ce8590d2c3adb23cd6404145afddbf5e18654b398ac0a0ee354b6cfd254d9971412c302b57afb73e644896ec74bdaad3a21c268c48b37a0cf7970a22e14a95f72addf13af72ee85e567fb98daa6d94ba88d6c8af8e02bfc037fe0624c438793677591146cd48b3da4d2330c7cc82be58818b5c96ea094be4a314c8c1c3023340d48f417fdb66ff71bf86748e95ea77dfdcd27d5593241e172d745dd8ca6907f47e6b207103a1fc9bff0ba9cc5556127307bab4dd8a9cd71f62b579ae35854b736af3d2e7648230877b9611da0c0e657ffd48e0e7982e1489b6a9a37943e6cb947bd2e0abacc109c9e01f390f28eb2bc2264e2e865c46662734ab8a718d3d812c8fdfc04357462790a6d2f5ad09b11f9c4954bb8a61e7f7176ffae8ddb6733acd31fad0b5087afb2695f9eaea944cfba9505200d4fe8788a724e4aa60b165b54c35cfc2ea275976325c84249e203d793ace9ad8001a255ebfae02f564a79ad3ef9946c2dde6205a2c3b0fa8cd8e0b431bd3b0ff968a9a97cb61f2737fb6ef216ac4afb121d720587325db079dc26956af3712406d0ce60be27365339434aa7b9ee129001b0515f27e5434b9ecf0e445ee0a45d3ccd7774a54ce036ffb5177730971fc814a28953fad3b536c531909042ffa1677de8fafac8eb7db383dd9a88ea4527f70de0966b2d3bac048cc489c0c0c691913e79bc4f3ba6ac385d012978628ad122098600e73b68eddfffbafb4022683ceb1c5d689808d8f5a5fdc8ffc4730eda3426a7caf15d866e5284890ce098709e86666e178cfd0689f4d1a747666fb7e7ac084cd90565505ddf40fc09aa3e8bfe26c2db1432e60f0eb404aeea8e4249a6960a303c3fe143cb55e45564fe5b0913bf8f6c982c4c1f7649985707ee9fdbda1712afd04b600ab380d70905cee858094ca77f0b8a595709a670dd57b8faef6455179804b0479438dceab774cb4d960609046dfc54837771e26f656d613a1fb1831c84c7318ef1b588cdf7d909e123c20359af82e3d0a9bb9d1d26554b009804462a6934cef992bf0765f824d9986f3aeebed385f8731ead30635a6ce040c50618089e2e038bc669e32c3edcd131748319cae641a697f5566484a5b5504973d80b1b3f4cc0d1cf90e917b6ad033ec67e2604689987b38edff98d9ce4bf410f07f320b79c6e4192eec6ea444dc7a96af93823e5f114a0a21b343b8b6ff1a5f767ef1e0b9c2c1e64d85aa608eccc55e23d1c485f9a020aef106f2d718294e71f74ce1e49aab051f13f1142f9082466304d55db810f59e935482b661e03bf7d4200a9033ccb47c4871c61b9a42198ac6093d8d541d270d61e03a20ecad6e817cfc0ef30636283d0dcf39193aefe762da76c6e097f0a739c646ae0aea623666a3c85850c4f1220292cd457719f4071a133247a33eca153bb23a86742cd45e9f662cfc70b8dd504c56ce14ac9281add81dc778f47d84a27cc8d9560089a06c0190f50ef0bb97e18ce7eee5a4b05824fc996d8be0be1a8607a2dc26b9081af9f4c7cdc080ad4f1362d634c656a60a00a633af090d6e88ff387f306dd112205a300be4b5e175a0f41ee2865da3d79bd45526801e5032bd937f0f233f1ccebce99a43140ef234c7aefdd9c4a8f2644a9737d9ffcc545c288e8532d239c5176e230d3b93bf805c0ef1dcfffa75d1d2239524bd3a31b750b503a1e80c93d53457ab6aead7905297ec80f9ba9766b86db4d469ebb8e722662bcb84f4fb56e4030d482e9eff9d425dbade57b6986f552847e2575ae031911abf1a32e564a596ed576d3485de6401df28c94d9f4881155e58c6619602c2a468e27ba9b3df8c12199bfb949cbba410075704eba55021d5b95af43bb0cf783f84504a364fb940de65bff7eb7af405023a786e30bfd564da19f1155cc39b2d3f2a6af67b3b5204687917587c932b06a1deb041c5f0aab75a685874b5e6562c25c68e73155641b3e66e5567a553a04ef0ce570707daaa4b832abde636ff8a551512fd4c098fae9788026a28929ae4f3d09fc6fc052215b7b72f281ad73b3d4ba061286a713532fc71c54d264dd88c40a139598384a5b5090ef33fe0136e29eaa89390f81f3f46c2e61bee9b3fe89ad0025fbd0659972ec4213a2c76d87aadae18718d14abae030c5604ca57dcba8743b00bc5ef116254de78a4bbd1cbe1c0a07065296e671bcb8799caf900f24ea440f1ace537f57b619f0e5af6ab81ab1f229b22e47a9f9c6f8ddb4af9672e142cc4a172c915ae763d93adc9d47aacf9ecb9564e662c30741ec9b5c53ba3683a99ad800cb93318c412ecd8cc68042f1b2bbf47a3057d98ccffa1119780ed2975d2b031a9e72eb5031d48e4f38ab89ec302f2680e5bf6cdecdb20d9b9b3045f14c414a04ce171fd95093b88239b7b7d2705e385c0f598c8d188fb51f46f9f7405c358a2721a2fe172c8f37a88d02c0480694867d40e734f04883503f7acfff6542084c17e4a9834dbbb1f0a1d06ebb8edcb614e50f4d9d69762f94874d04d6fde4c0752d806a1a856674ce00b07a6f0db2f0406821ffe52ee7bb9eef56faf3b678a70b0056caf14c48cef88874a5d02c859c85256e1015202caec5998878c9acc0551d06eb603def682de60f7f0a6b42ee075b80ba4fae308126cd782d7907534fe71912dfd5fff73789282bdabdc9299d7baf436ca341104bc53cf83e797aa7f785101e9d723ebc25c4cff66b835fbe4041916e7465104f9b98478565eb0bfc2997fc0567e4fd5c256c85f68fac3ee24e9319f14d2405076e2a72bc71fc5f068842a71dceac206bd01bc8e72cb790af500bf02e65223546cf33ca170765f805f944070a46b061b5d11b40ace050d8cc5feb16f7c4c9b438eac11ae5981a0618f1d45c12caba43cc1af67eee753b03836dd02e4086eeebc7383e62ff80393178cddfa30efd36409f3cce15a1f3ac25691bdcb29bee5f05aadae6ad7338c675b2c4f96506539f855e4208731d60c46aeb34602b935a8f721310d9bebb0d1bc55a48947829e0aff2871271b07d65be03044bccd3983cc2991e66e1d4fb79f4a45307913326330c5496174b12a1904fc8504adbf956d3d00caf4d042a1c23d426ddfe81de04110ed4e9d82d413131da701c94510acc7894d2c5783821fb1b2947cf9b9fc815c703538175f3c0cf2faad70dda3af3dc6967bc4162e5dd3f398477700f110e24187e1ba95407833ed314bb24f38410df33bcab59155fb173c172220aedb46105fbbe750bfe20184c07aaf6ab485047c7d2fcf738e5519feac385e7d74e472e22b580993c10199c9a264fb71fd89e7cd56eefbbda1cdd63962d7d30107481532c63d7f82930de9f0e3e2c7affb5392975b7d7744702e68b1766ae7de38f60352ca38e67916736b2da3e6433726cd27b7f17e76769b44a03fe3c4e2cad5379c529ab3694223c72b67c02c60057aff7003b3e53d1ec59364a6fc561c7b4b184db2bdd79fdc9704edf0916ff4740514c1987e2f2168cc54b9dd340d02e259f99ce01ad9d5d6944dd52a978e6ba9bca1d277dd21ef3f6928cc2297ee6e76ad3256a15f1009a8956ccd68a338c2e099e5afe9b3365e5818198cb107a9f70be8add7a0ba76731923e8dc81ec699a0ef0fee7451bdf6fdd54f16e3c37bbeb1b23ab07d11a887b6a1318ce8ca89bdd326a988dd00d3b4ae59c1bbbe540469b4b3794666060925b7acc6dab414837c03d8ee123e09f87cf8a12f05c85f3ad1821edf8d12187339266d5d2a9bfa63eea5604415e1d7dfcb7cf9a3df030cdd014c5c615e340cbca315a89dd69f61b352c60c2ac8329eb51b4cacf86f3c467c0199eb1199f7093628fe7f809ceb200f8b262781c164f9e96ddb9ad897fcd1822bee0bc8df832c92537cf2cdfbc0e4d83553deb8249c5eb8bc198f1ad13f00f38e855883a4812c8692f4d277a5639d047410ad23a154c3f18b76e4b062a2f82eaa89035615db2c1f23e3ec233ad71196965522f67120d986e10b52475585d7398575cbc8a81626024b8181e049e4889d5939fea09166d9fe502e50f8e75b8a39cfeee63386890495f31496f1b60165d7afba12d993e5f3dfd22b89a1af0fb254d1151f1b263b2cc367575e069b0d0833cedd493be67d216fdec70fd7e4ea0311ffd0c622838c02297e7a5899d85528b3b21b8128abb3ee080f203fa7ef135170f4eaffc9e1fd0ba8712161528fee2c370ac0f78a6325a1f64768309a307def9f9192feabee851b6ec658ab933729028cc58961923dde03438a74bdd19ae209d65dd2a2d1b2f801c07e1b41644974aa3a9de8bb0a0054e1e4e494c2330029e9121930c07e1bf8045674837a5913cf33bd338e0903b9fb9c99a63584d83fdf0ed16c4174334f4a18138d91a0b174724252869878ed251e5dece675925eec92cdcb8215e5e4962d21989b001f106ea3d1a5143133cace9939d4bfd6b263bb3d9ff7853042f7766118940257dcbb06910a1c5809866a3b2d5d12fcce6ca65870c1f9173ff5c9603622ff578cdeae9d93d70ef9f3be40e103ba76d08fa6093dafe5c1b5590a63b9e6304326fae968c3e34f8880064566f2a96ffbe50721f2b5da52d43a5188cd710b1aef98bc4b71623fbf42b824b15a39044f7c1875e2be037b0918772b3a4b433e22a71b722d0878620af6721d59a1053c50234f4f03ca953a37a156fb6170f0190ef5bcc4e73421f17752d6857e24e8a6adb26f80c34d96bb8e613ce3918d4ddf2ab74e92651bf9c4c7be3eb2ab58693be33a8c3bfa0d51d077d9e2feb526241c058d33d0d04a34025d6513192d57dda67ec1800bca63872ed03c866eb1942db6256ab2fc551659b6a6ac38c0d3a218537151ac2497e6ac5d1a6dce982811fd2c2f72c2f6ecbc03420498f9d2d19c28416b931d31eca2a50bd70286778b5d5b3e07a9fcd192fc5c1ef2b71ae66a29d9b86c423a24c3f3d6fba8e8b6fd3a3ef2e621be45cbe7c8a823068cb6169ed79d25885594c68bb5707a95543a8dbc98c83d2593ebf226e6fa831bff91f121397319f586173e4fccfc25eace77d338669586066c1d07f923cda6df3e64acad98fa0c9bd48f3348d5891d153d215f0ef0935e04b3d439f7ae1db0ba9c09160acf0c2dd57dae351e81a229f10550f78569c69d10e0555e194d2feac7c390651f902cdc61a031e3cec9d194ae5a1abd14ad9fb3155a16e4c965278c08c26a4c65b9a7999685e602dae2f686e6eedae39af4cf3c1991c34e32bf7e092b14238f99eced1da6cbb658e72f399c5e2204cc4517284c2f2940346864a12b66ed991e5d9e54daa380c27ff63265545bf809f1cf6040c62d1dab6c55cbfbeaf1cba651c23b6e4183365896bb6a75426fecb5d7e8588625249f65de21a1fca4895de7b13d96debad13bd97edfce751359d75e90dd0ec69b1086a85f55e71d8c65942a39cd57b3ddfc66d17720f40935dfa4041ac91b02ee2e0b88602eb808552a94e2596819a2bd322aeaf970f0b9f25520c979b5ce334e1e4c41062005db6efa42f26b3afe995f3c612a3207a60cf0f86d9afb2f5c3c9e2b98ddb4cdeb681d7a79a2a9e39ee354e55db4f9489931d684aa43609c8c51b02232d661cde0880715c6d7b0d9bf48da01e99f43d720ee4b6d06cf67d1ea57472ad7287de202f92536196e5e4e7efdc289f2e8d29dae80c0ddd34e43b06b700010c6d2dd5eb40b30f5c1f34b1cbf5dd29a9c6413b404cf7f4d5e44c6efd06ebd2f61b00c6a9a23c2ee4a912a346bdeb353a24bb54066c2b48f603ce10bace6fb2b6b2f67b01b1cdf7418a0aedea179e0420b190977ad849bbf763ea19ae22a641e5416a795908c6a5bbf2fc3376ce7c9a189e4852d0a59b1b31cf12bbc26a43c0540859ec14f887cd35eade20a86905e21608136a7b9225a5aa16a9a55318071120ea66796e1e296f0eb89c9dd8a77ec55ba003d439189622adea8684f9c0d7806f7f6a167a329588ef99908d323e4038d7888fb2fd558cca93ff8a8e360148e0c47df63964e87a2f9e5dc456e9371cf436f04ad4ba531b2560a9160fcf8ffe21c8a6392a254c34a14b1fa07415cb779b4a97bfebf9c856716c497c04ee020dc2ba8db6e64064f6a12864b950b4fb8943d07c192c6b3c64636d04e920242f0b821e1e5e9468a4099bb85d0dd20a2ca0ade28850e0c75a29e17d7ea104b967dbf1a0fa7c3ca7ee2fab640e3993c5034cc9db10e99422ec8393af23fc1244823540c04d3650598e70a47652d82822dbeb095bb5422a85ebe8432d25a4b6c4bf1ff0f6522a9ab211ff1043c79af9f816db1a3ca129f5ef5b11c482f7db11867ed3405f151fedc4ecfc5d689a334802fb029a00bd9764908c84397b8ddc832d2a6ec0489befbd5fed24727e2449e82ac9a1405d9a21cd2f57451b548030bd8b00cb41b999bc3ddb385c86dee4bf26a1c90865a9cc3fb683378cb26248fa87424483120ae64deebee903fba7708211f871846015acd4d576f03d8b04662244b5a64260fded3e599ab32a66c2ed158174d3c791cf146feef2486d3458fdeb49f2d2acb27580f9291d1eff894607e2883f2efd696c8d34ac9bfd4a586b8fe056fa28f6e10aa24e33810e8b61312f869c995d4aac65dde2d094795668d54b0dc219fbc062128339cfb1a949f000a02d46388c56437439dbf3d755e85880f52510c59882120bc9892c6ca86d070eea1abe8c2566f701490a9c58012eaf3b6d6d126927348280e5413075437951f5f113bef1d4df52f41f45df426552b611545d37697ff1f0292dafb57a127f033d599a0e08071988f73608d79181ea1a4bbae27414bcdf315cebeeca215eb368de29fcfb40816567217203a1d1597dfdcd00b5220a802430a9ad88662352c218a6dcb7b7e081ec4fbd5bbd0bc983b48d405bd40db07970719874b5f0f23d5cc8242a11f54eb44441ad3afe5b59faf20a86bdc466d2af63113b7f9ca89a8532daef2d3d094372129a5beb0fa710ad73f7aff437585bfa2f2153d127117468e302cf735c858a1d9249dd51377d0c49e5c5087cf8c8e859e80187fd2efb29c999cca326d9faa50bee31813c4610afe50f2e78e140fdc05227c02223b0524a911ad80988b7784337087a40dd271d329432802b6eac492e7e57a0193fac4b5b1b03a48592cd4211acfdfa0916177c1ad875e3b7d60bea7f023b72dc0b82e002721ffc03a4e8aaa8bca823e3323373116bc9d3577f80eed7df61521e0ad3fb1dfab09b965cfa000dc910eee2e99a86ec7debedd1bfa6eba05844a1a880f9155c742f51e7b96419d512e48f56ba3c42b5d7dec49c3500b16c2084c025f86494d8ba7df6789e6ac2df2131f4ba25d5d554d75b2cdf1eeee4e44d6e421d8f4dfe37685b8da071a2112f4d690cff5e495994ac9c2af79f6d1244f984427f1e6c51dfb2011f9d1154ba3d84f2b23b52897c7f2f4a10820195af3d0be462564326ea6a7a6d7e9b4131408df2c8207e26410fbda6e2de4f8977c53fc0290", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117011521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da04570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a7110ecfce55cd472966bf92d95eaa2ca0aa89a06129ca6ea67d7b5c9e26e4925e01de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811088db0dd523e2dda90a02310bc443e2796689bb889f1a91f22c7e910915558a7279cc5790a050ab4e07fa5176b9018996360291d94e5cda28e417eb2fdd3e8df" + "proof_hex": "0x000000000000000000000000000000000000000000000002b30b0403aeee459600000000000000000000000000000000000000000000000db8eebcfc671fdeea00000000000000000000000000000000000000000000000af8f618d2adad1ebe0000000000000000000000000000000000000000000000000002c5040e9c20dc00000000000000000000000000000000000000000000000a2de63e4ca6eb0e210000000000000000000000000000000000000000000000021ae69d4a65c7a8e900000000000000000000000000000000000000000000000a005bc241e7d733b30000000000000000000000000000000000000000000000000002aa8c45dd85d4000000000000000000000000000000000000000000000008ff1dbd0f4b9b41c900000000000000000000000000000000000000000000000a46e1fc8b19f097560000000000000000000000000000000000000000000000075006eb06b9214632000000000000000000000000000000000000000000000000000093433350ca3600000000000000000000000000000000000000000000000c8e6e0c4281f705a50000000000000000000000000000000000000000000000043c924cc53c6b0e6b000000000000000000000000000000000000000000000009a11c5c7358610a3300000000000000000000000000000000000000000000000000001c36379475c900f5f028b1ca9b05f4fd64f348afe95c048291c2608303e91a5c888446ce265e275b7caf50609f9aed4ce3fdda4988dff1b9cfa755d22a06ac9bd70fe011311d3021f96a67be4ae494bd7f310637090a697383e48e1cfe83d199fea464581f5b26f5d27b00505d5850189be9bf43d8b14ee58b28a16c87b3bf9d5bd3dae341de20a977a63187eb07fb817d6f32bd7e353ea5f2e4563e05af30e41de8d76caf2a05fb7411e0736765ea872b88f93642e1a0a122c2beda192429812e49797d0d2b0b2d57c7f9a3d89feb36de6e8925068d4cf01c5631bba6ad66d9dfea89eef42507b82d0cdde68023f51cd886423293963e553af03761d6bdd7e82c5f3f70581726faac8f74102eb6129ddf5940c15a1f473a25099ce9aed7c8502f304535e73f02c89d7edbdc59430a931b60bf65dba6ff6c4568fcf3361abd1e9097cc3446930d256472545bebbdf0cd8202744ebee6b32aded93bbab7c8b07493ffe15d753c2e767d9b71f63f422e0d8397639315c74a3f459246674a141133d6068e91cd04236270fbc4be415c1b3fb0340ab79c0aebf168d36a35a77d26f5507817a381e82997d3d4e6bf47cf282de75db04e846b05a1171e1b27b3378df7f46c8a0d5f452ef0980db9a94784dc6878f8768320b7a5f1327d5a0398b9090cde4a0301320d2c66315a1209ffec932d6318d851ff138899b2fc2f20210b9ba6487e63272dc9292a55757e58a10eaf53bb32db4bd956cb6ea7db2f7d3d376ffde54a0c481c8e2b91e9e7d106a81edff57b8a49e69ba1cbce8d8ad7a768f51040cd0760adda1f1b057d97309b56258908e7d0b72e311d563d0f33a89b025e9b29dbae8430af6c1d5dc9be373e533150316757b50b3d7f39cb3fb983eec9d368f3997f12567c3516a615929225c27f1166fe0b8a910ade60dec004d39048fbb5e348d1d7b8f14e167cf8625eabdff7f7262e220e7cad7c3169f11ed47467b15790ad046ae3ea490d5aef76bcb164504f3b903fdde68d660bf30a4a023c0639d3b0df5986a898291ca9055a779c358e4526204e571e880645cb2634ddb604f97aab892dd8b2c61206a8ad742b79fdd1cf600bb1b100e239a25d525acfe6fb97a5c0a13dccd21c3e1be9395c5f4a8ce11c7c9adf0450a3dc13c753c8b0d37894fdb03746c10c45e92b11c11485fd5cca6eef8495c4fe4a8e89af5ce2321dacfb3de59466cdec2510156f196cfcdf53325e7070710c87d4451690eff29e4d32a8e34518657116d7ac1562534b4b1a9267d5813b831d136f643f2511cdf969567bb8528b3b509274e022485fa86f947eb6caddb7d6aa964114d188ffd141b79fde4bec77997e8de82e1a22e50c704d7e5fa81ad6470e6e610932071304bd9547f446e615d36f44ab582ea1f51c35052fc32ef3fd4004dc82b1bf9a39b670cbd3064b42858de6d62c0e202f260e6c01dc5da58f0f5ca5aadf80c9a9810e700b5342d312a883895208ed277759e4d19186345a6d59fff1e1b2df1b9ed20c49e08dba5d430c5c889d232c0dbec12765d1f90c1243fec4c3d67e1d37e80f7783ef9a1828baaceb6f59bad6211ce93b78dc48f06df07f27a2150b2b744cf2a5a06a0df954aaeec4161a424e2eb3a35a6b9f4e985ee41ca8ff60b38bfce07c646fcb9cd13c438c57afced26e28939f478f3c70bb968c92a82a5be84d9453b1121028cc087d1d072e7e6c7c0205fe83b1a01ceba1e0e90bcce21bc3dd3be897d503f20f49b07d818120c8883900aac83216fa6bf485c26476d03e6cad5643b6c5cf217f866e372b1ad25028ac239027d059b6b09551123523e2a5f42d33c4b330bf9b77e09785e6e48fa657361dc82de67104ad93500c9741939e8ba447f6dc1fc4191770b950772b9f1c82772b810282ebc72e0e003f822c1a213b3ce88f42565589de9c8a22cd5e3fbeda3c210803bc3b27db480707058c2df76c782e29c4ec3ed4258affc6790d85b95c0102af61b154df03951ef522e8c2e2d7c0e65d89bbbac33c8119dd9bf436def3cc2b3767985ed2bbab59f583232694b3a362918f9f72a99af2f0f5af2efcc7697c2d2fcaf7859f1a53585929269d86b3c8973039adcfbbc308fee491644c480a7c27dfa9fa5ef3bd09bffee4577c9bbc3690003ea1e3aad93e92fb3d3049d533190deaec9660872e7fcabfcddd95bc009081df6bff849124e9e9df109416fc751b0d2a98b66c8baa3500d82452f976c890f25ae6399064f155ecdb915f4d008b7a002f0bba3996e8fb749742a4b69120e029e62ad24d4f998cb586da076c916cb321f43d09bf02b75a56631867d73c77cdf9da4de3492a4b650d78b39b367dfa510fe1e9c7137dda94b17f265f6938deee104649c6163212b3f9be3a66234369c113a8e3a54aa3505c8bd668b9bb77dd5eec4e994e0024de8f3203d92f6f301e1223b57e5d4a69b68f6d9c57d3fd2dba27ca0bfb145ab0c29922b9a2b129f0e3f71fc82a5b8a4a881948245a642bc118320f72a2723d6a7a9ebb1bfe185529acce021f5fa2445c3744c8ac48355df7a41a8520486e45fce375c3c6fbd3b33bac3c2e5e29bc45ca7fd84f34893fc756f888c9db6e8d1b33d731af38f622c11c5e180602f9f504ce236d5dd7c1068c9f472f64905dde988f8e4ae7f199def886026f21843689e58f2f3dfe48f8f5a637b4a71284fc5706e649278419b7c8e538926409cfcd311cf3c556de1e275dbabc9e67658d311ba3cb5c17542c186a7da2ffd129684a2f9bdc4f2ebead34aa0cc6f94cbab3aabd66c8fd8bf65350be1ef08abc28a673680e3f8c7e41567d1ded0d1c173be5f3f0c7e080b2f1545630ef9644321ed718522ab1f7584f6a4ad83079823dbe5c55b19701cdf61208e783f6516be01412b7a8746a2c86c2cacdb91aa020b8d1db64dbc78c4889165e11b4aaf173202bc28bc70e00bc24bb8cbe3839f97793b66099528624e7d1c7fca00b911b707b19bc81d8401fa754ba2a05bc955437d15102dd440d1969450617d725d1c0baf51b3d8ba9c5943f28453d574f21fc2f9a08f9752aa61f9188a9cd8574593a82ac1794392b927399e2682bd77ca428316d1a2a77fd52d12be4d3f7fd57b9b33a68065b4112e464cbb09ae6d219c9acc029a295cd67fe60517b04596e84de3cbd1602082cab3609a950d6731f100664632ada637279bec7ead801d854a2b92a322b0f0f6f0554856fed457ba9fc25583f8f1ff8d03d55205a052c67db1a02282cc80559294b448cbc117fe65a32ae25d61584c1993159d4c2329f89293162f89ad02d91f842933045e44515de74b0f6da4625efb734d885a51865862a4c2d8104682c6d83cb7999c411b070ea3a5bc939cd54c26d43025654b66025c683116bdf06294b2eb9482a612e3d67a632d391609406b60edbc28b9c01c5d5b43796bcc3d007d36fb02537fcd6d3baa7bc9c3859846fce0d7b30d65860a5d8d27f6d2718ed15850e16cdfce67e1dc1d63b0fc45f1a17a0d9c7db71f689e3766e12786b8c2a2d60dc28eeadd07ade9fbf8447fdddc4f91d05b10d136058ad481f9b4a7e024a17029bad7d3a24366de116e8077da5b101c14da26867735d2524bcb9864778e02dd5dc33744ca7e50e87806b9d773d44dd60cac69135a5d29dcb4b93fe58551024ca2bae5c4c5f6682cb487546e40abe929304b714f50ea72c453cd38dd5e8692c2ae119ad1d9ef3b605e627d35720d26cde98e914c811cc457ae3cd5252b79e25893e23371561e89a731da3343d9ceeafb13593ccbf355ea34eba49f2e63c47289f58bd3e1fdacbb319183e85582f405592fe19656cd0a23a1cfe79d906f80d1efe414b0a22a209c332036c65cc9d46751c183d8618b839afa04eb21499cde40a98b16c72709e82be7312a6085b0563e940fc80e1cc10ad90615a040be766a204743f7867ec779c8a1d0d5fe3de9462f19bfdb3da14dad8768e04f13148df882eec36da9754f273e15636d808ea116523f0d567b5316bd1ebcf6c4de936d03908774b8f1d132154642b87c20ac9cd2de8088f1d0a55de6db45a75543d1adb2806b5975df484e4db990be106045d32642afbd7a492681e8681c84ec41bcadc6e1527f9ce6a7a670a20f260ae45d21aaaa489855dd747e5340f01a956152504a31ebc51009014c5b28e9e845c3a143d89302c81d5f94bd499250d48ddaf47fcec0afa7a61480263c5273b8acc667e784402be671586cfdb7b4db3f8a0f5e1fecd2aefbcf44e5f04a3617598869909c0aceaf0951635951ed4180a0a9b50cca1bb2b5b775f56a5b46a804330edd52fcbdf477485ad2f90e1aa0e3c6febb4f769eb0a4e62b793e1ac9676a35745509b1b9137cadb86a1fe28b0a00b1ede0523ef0229d5521820274634a635d398df9b85908f4ffd8533407e995995fb6a6f4f8bcf15aeacc9028dd5d83f265d2fe95d44afb3baa593f702b1937c3a27a40b6f02d7058a16f98da0cb85c8c6cc07d602960eb28aed5a6b5a8005363783c4c6ddb2010fd07d26daf701ec005d7765e9701cf8617925e4dc28744355b61d1fc2dffd272b74a06f3520227be984cbf13925584c110fd52798ddecd811ade62f107d939c1379e3ebcb8d71f09dfa48f3f2be201f2cefcc38345ae3ced3286cd06dec45e9115b345f3e78a3ee45db7f9170064864d5c09cb0fc3e8f5b7a763846c9b6a87d11798becb574a394dc37646282a7f7c7db3824cda09f9f8a5741937915979ac90b4f7b5de4d1eb4a3b7d6181a242ce8efe9e99513e83b53a66d91e85ac3412a40f2ed30dc99975a2a8aef2f11dcbb15473e41cbbc7bbb7adcfb89f3a9aa708170af029423d5e3e33e5bcd5bbc040ef8843868fc9243d05539ef570dbe1e10aef20fd382c33955f4bb9721ff64b91dc1ccb64d1c39f422bad3029fd747441ead524ce950b1a581caa7944b372cb78c0f9933a173143d3c99f2ef5203bbd71341e1b580d87c30fdf3e28038e52e088ee5d1729da0d34f16b6485002e49708b2af80a0440fec31f89fc13213af1d473835795cc991ac856248d6947c032bea69585093d3901d6393263c2e887bb58cf5f9e8dc4da7a20bca245b87607de9ea0620f096ccbcd54b6f3921fca613160e8e406d7331807a16d33650e418f91a75a1b49155d96ae9ab1de581aebaebcb2cbda8934bab4be6568e7c921e6ee1e9ce8ac5b280bf12e8b4976159f25c05e85fd4f48faa1e5042455da26784f0378846b00b1019ece0c5443a7276dce936601960c620c0f48dfdce3d7158691ade1f4c89e8e16e33e4c84777d3b261850f67e4cafd59caabfaea137826ce0b785a1efe2a1e308bf9cb95df87413d1bed427d514f8d2f1ed5215cc8cc01b960d2fe3466acb780ec253f4c5b3916132fce27c7d53c269e49e0d5f5cfc302c1815c83a39a842a4196b71ab05ffccdc4453eb6121cad50beebc9506f5e6e7a86325c089c1505a5d1565e4e200c0f947704862ea0bbb30d55ef3b0138243795391ea5660caa47102215bb22876b29def7ec75957f10b4173c7f04355665519b869e5d51c8f7e156b05af22ec3dffc3be18ba8bef4b6112006d557dbc866a984f59a6b4850790c67d09dbdd531ecb6baede53b5f37ec5bf114667e1d26ff037959d61dda4206f612f01df169922373268b1e7e6fad457ceafaf8c9c25e62c37454897d9732b6135712bee728c4eebc919211002d9c6f8735af7b8c0a544391a46f28c3a889d0f2e7a104554aca10104e8de7cff3649c20d0b1f54088096a7532b5019e476647fa05e2080741d0dbe713cbcaaa2afa2303255df9d8e0d391a6532b96bc449d704437222d0edd1fc730ed424ad8d4b5a39369538c7d586ba3e59184dd5f2902ed1146e1a01c7b01250ab358965ae82a0f4528fa5ada19814df0d73e4256eb85c0782e527f1f4d0221ad31b598531d3812ddcc1499f25e1501865e17df02e1b8cbc9909234c2802c41b8f3e59bdcaca7bca354ac8a06eaceca80e576276e089cb5f29e61b0d1e6d85b56c11df39d49c0836c0a3db33fd564f9b81b2dab176127f2020091e84be762866d62955eb34adfb2c036dfdc79652bd5168e1eccebbc106c6e0842a6ad0ccc37f360dc838e8fd4104c57d8d74b677ebea4c070a1d5e7a7fc219ad1169ddb19c6d2d9d077f9a31b480259d7c4dfe8e603ec1953634fc56a67550bf2f4c127151b656bd10e7db9c9b4e19e526d93e483d2beb6d6f95d5dd7dc4ef270edb572eadb4f1e9abd0639860e241ae44ee499db89e92a7971670be22941efb0fffb8af56e85dcb0a48c09ae10131bf2a64b0ecbebfc772259c580506db69ac2156fe9c7240f7f66d4714e18bb20c07f44dc0cd84f0d9b1aa509b66ea5e255e0648ed0a2962ede02665952151a54e8cea60f1ce897f00a3abdcd6fae82e74b6212fb5e7fae591d70df03de924b74918ada838ac568031697a44c273484fdd102cdfa567eeb6b89ef0d9fa657e8943950256b1e7053b0824e4bef3666272ac0314f463083e7bb01bf0a666e722b81cb549429070f0cb17f6cdbd435b5a9beb8a2e4d8baeeb9fc7abcf248cd7dc6a01075c51db5fa579aeeab43a582b47399ced1f5044f92a873e6418ba412cfa0a8a2db3314585b220ed790f35ea358f93a7630fa379be8dfc67d5eb98c3aab5c2f533d6750b19ea957acc6bf9e2796920007f14afd5d25310bf4bd31ffb348452c2d021e55f237a212ae30aee3f52d514f4520a9af18e31091497248753927f99eee28859559b268451ff74aba9de02ee6b4322d7570ff915b07f2cb9c60a3b39cd3b93d33e3e05f149db531e7da3325e7bbc1384880b0aae07a0d00123c89a8dee209bd57008f71bd1a6a5778e3dc257782b1f4eb8695e6c34c7d409d48f8dd796db2afc7267b6c14299be5aa8fc680abb4d14671db1342e867fff13ead25dfd0ec4deae57cb9c3dd91d62eb03725d734f841d9749ba103d39a29deea0e29fae6d44193eb8daeb4b06ceb487292619b51b15221ac506544d7bbbb235a8b1e025e6f95a55d4a9da6e2269cd0fb34f9990ebdb13cb608f09dfd348a611efeb3a20ba7d4911414ef238b948281e71b4aee677131b1bd90b800ded9e14d190e183fc69564ae4f8e218035a2ae98adc09e2dd8911283d549ab1fd630223ac9b69f13ebd7edc52739fa0d86ae7bc7edfa3923fb49f01b6905210ff375d48be2446ec8ad19fd201030784ae30ecda07d819104ee91e060649b3d0c9e9e2b984ff05a62d46c6e9bbdff96c343c82a6512f86de130e771a1a878e6a41a6ac0fc0788e6b068a9acdf76a9e550c760f1a7d2054f006a5c71ac65c82a640ecfdbd3853275aa0ad3206b715cc3ba60919d7a2d307a318d84729e3e17fc47339081b3733d471aa0c4a156a7b54b1b9285d483229a32bc6340e223d87b4069b04d0fbbc22d2a54ac37af4289e9138d2648099c28729bf009c2d28faf64c8a8042fffd8bb899554655cd463010c1dabe9bd2d7310594d704f1e10ece778e01fa0d14bc72a9095f381f288abef48d73e1c8f70323019bd3790cf90609f16cf84220713056f45f8783d40e5baad22fef47a7e37b46ff86b16a7050120dd8a0182fefb468bc5f043da2c84b3c3c1233743dc0d3f812011ae063c60c20907d674016e758411738d08cb7b38ec5cf2675460d6e4979a92f1a6b69a6d80f09ae9f8647a6057406542e4b5cad4d751ce4851fe2043791ccf35977c9694b27ad7a862ed2c107a8ccf062000dc496547dc6b4d6f3b0ed91a689dad868bb70009cf51ecd1b12f4f015bf9b58c6a3473e82736e632d1274d46e8fb9aadc92ab01e0c7c4c9d3ffe24d2a3ea98e88d74bca4b8fc56d7d94fab738c555f2cb4ab724de351101cb87df73b68d48f182811d7bd724087eb2d3bf773a92ae7fb3023d05f7d92e780bc1b98e1bcfb57bbed3078e50104684c04a0297a1f054585de92e285d2c460dd30427ebc79eb4e26c0479727e1884df19ef51e10dc42db993de31190d3d6c988c1c3dfa4e0105fddb7ac35563ecc72a4a196e035f91148ec48a4b119a13ca4a98e0f7a165f6547ee9750bef23cebfb0e79c139d02c6565e92464618471f2483429f5093edaca8b65ec2d966a66317e4fdfa8efb3df367f2511fd514737a1a937a481e7d8cc9a03c3a60841a4bf7f9754bf2fbbe5cb6164ab948c00b2e306a251f23b43d7d25e038afd9bb94e90efa334bea6c0c33b9e527b154f0271d9823269f3414e5b7274cc402fcf960867cc389df326a885484a21a1730d30e0d40999fc462c52b6a00344eb43a6e589008ec5dabc7d3b7eadc04cef996180edb9f5dd33c7c3ec5e30aa494b252658237e1f49445b383aa5221481bf279c824a408a0cba2b07ae7260ac3a8b16e1d481d8bf795611beb58cf4994317e4a8a1f43bc309f0b9a6938bec0f485cd9f959630aa5929c9a94e40f0fa18f3ef52d225323ffaecbab738f0a11859f83e7823fab2368e875eb74f7efdccd9d03906d70bc078f6741689487c98d8823c61dfe17c86e49cff25367cce5f92cbc602a350181120e505a5904a146484af259d3cc7407766ef0f9d0ed2e8c8eefb1455df430b15d7a20b2b8ce47711235c340fbc0f7cd120ab132ce965274b695e8384b0af2f74f1a79885f6871a7fa28a0bc7c26363be0e27b50ec7c8fb14c67e80e2b47118205e0e8472e0dbd61b66277d3439fac17951af5ca2d6b5af0d524ccba21b6b2a6051dd94a1b8d7e7704680ddef09a1b1c4419f2a9ec9820da589e9b20a70ab24d89962987ddadaf53f6605cd3710fa08fd60f3daab58c4ebee1ff8f0c7b88616302942ef09f82722e003f1421f3722d0df287cad3f920171162ba91a98d6032d74219ce8c4fe3c7e33b0ffa14c83434d58870d116e6c36559a0c5276bd3ede2f133f4b9b09ef25e76cc4ec02d3a62df652cd9034f372c30b66891e912e1bef1e17181b25d1ba2eacf2827897b7b2d0c5995ca072785082d059c8644febf9771752e9b58e4c98cfc60c257c9111c8b2bf6734c7ac89d2bf8276d6eb14a2be82084e6fdf8ee543c1a3922df2d173bb2d7a7124e55a2d39ea70742821f08726c31b6c80e32786142546b5c35a2a9a2441745ce704f0e55134024b64036bd28f8d2b8119e8dd0e5cb9245d8c9058a66731ae81cdac1c54debe97ba687b26ff00671d6b69bb7f1dd005c20971d3f86f8949ae90483fc8972830abc6481f17f90547263749df4c5def86d1923157f67fa1738bef66ca4fa004e6fc5b4af721837799245cf824a7f53cd08d243f550311fd4a3d959daac4cfc85f32604bb35af71ca004ed6051ffde6dc1b4fe2eba6d82661319e9cd3b855ae418d73cbb37864fe3c10c42f2fc3567df4971fe7a1774e33b8a72c9ff4a7c9c666df10ab3fc86bdd66d2337af6fe5478206b02c5f1709d87b9df0b8fae401bfcd98b33c30e01e93675f1f355765c7361b84de13e9aef9e4fe7aad73a2117c05f9714a0d361e693470c80506dc72a579afc28392a084af4bb406e51d53782e6ca9ced5f2733ad62194041187cf6819f495e3f36941d42ff64e68b479b4c98047b50d14e9d906b8970b9a1a85df2ebcd9f91bd9f9e23d6a103a4398801cf9fd35ffe19b1d22551a09c77e050ac86b551234e153b9b1f200e5e4d128ada63380f68aaa80df274d0bd10de6129ec811001ddfa3cda1b108abf1a10645def9a8f6089074ff283c624782d8e507e2120f8237004abe881653eb8da68e34bd3aeb10e4a19a8fe7684dcd8fb2160e98913519c62998314bbad28005c4a83290a62f9334bd58031fd66bb0db262019b4cd0c0423184d42dd0524ddca8871512144dcffcd066ff6ebf9298ea84e812ae69c3c6009d6328449e1f9eeea8fecb6b9e5c96a196891777b729dbcd009b11dd6c86933cad747984c5842202dacdb14319ca09f16950b4ffd8e8380fcfa9223d511d82ba615fd3506e86326f1cda9dcc1cfd9e6d5f1ecefe79eec809734300c4b915bd8b2f50d9ed1228836ea07a31376c0e1f4bfd053de47bc04acebc1fe0930c6a3476edb691858be52b6de2b01d9e1e4abfbf3d3c5d2684b4bfb7c657919035941a2aae3ad42a9e63465e499c16d2dfab35f248622e7e58e84bb84b71d01e032044b717cab2d569ebdfe7d5097752cba06245ffc7cad1b18d79eb62ba3118c1524d59d9cefeeac71e447831f83c6f290ca10481e6916ae350b88d60f3e2f0058bdb51489572e95e31fd84ca7260abc39f36938b4f87e6d4835f1ad5fb221a652e8262f018bfa3856a8cbd34144e6976d45a453f3ec06fd6e376dc4cee60c6500c3280e6ab4927048494018c8afd9bc6fbdcecff3018ccf7d03b51a6eae14b7f8520c39787f70776235f01793d7e7f745848413062433200ef7f8cefee602784ed98b2c9d273cdecb02ddfa18a8a661d741f3bf87647e3b8d25eba2360217f3b3108225afa2797868ceaaa866d663881a0008e416c5f2f80af7149f93050203465a7c2fbfb1ae9517fbaca023c9862be40b2c84f30b81d237bd7c3798c30891c8ac7a91dc7b0b69ed9e0982acb177108b867adebdf348d894dd510a0b7e1cd010d9147a550c8f12f60691d111628d9182c83ce9536af7ead503f2d86b1b07c40fabfd7c29d47e4f8720b7806cee007281fecdf927c5a8090fe200db873a0142f5b631901ff537e84ad96b1796bb469b1b11cbe42c2610dd49aa74187a4129a1854db8be04d0079d6f88c31d140eed4d5a1139b6cf42a9b792ae577cfc7a2879bf32ae3070900028e0e30c27d2c5d1e44d731bd3d2e6d6135dfcfb18572d107551dbd61f608dff663d88629c18be0406c8ba65f8ce3422d544efd0e1e9000dcdc683eb0eece6d0ba89d46022e636bc1ca13b2918d04900ec63e29f630e7b09d795652cbd6a6479cde8cdeedcac973f5d240002f8f6df8a75ecb9e1c6d2b609938753bf2c7da02deb6f5f3bccce5e9ca2afebf91feaf1265aabdbe2ad99212954377b6f55c3faa8c03088b93f943eb08faff93d5c958f119889f11fd09ff304933fda02023e503321f3311c3248fc12cfcd7e748e21b1002a39c02c01d09209a0ce90d8026f074000e455d119e776d349ceb3da94cf523de5dbcff0f48b0c1a8048441e7c85fcfbc208ede9faedcd17cc3feecdee4c70aa042c6e441b059a14cf0077b5c8565e3c03b57181839f9f7ec98fb603c272189a6ff3a1d3a4ab621ee17eecc490d3d3fe3b69830871d0f9879d3049ba97804e641d42ee31522ecf1859fd0e2feeff6c4515dec6601e327c6ffebc6958cdf901c0ca743f82214679087d9626e792e9ee6cd489e79bb805d2da252d55a99150fe19bfb245a1fe42170dbbb9e93f584301289b415aa57fa49a31d5a2373d55a75df54190b1e216324d0c11ef5e89f61d21a42d90763ef5a89092a10e1acfb7e523d7f3fe9f8efbeee02c371b165637262eed95245171b22f53990e63e5d7277c2d73a4c391af7f2e4e1c18101c45f8cae2939e17c2a4999e38be4e8683139cdd427060e6f476809b670f35b2c56491d7f825eba12d6626da300fd3c80da328f0ea49b767a41a88ab8321c15d42c0af8fadf6438d42bf60c3a2b3adc5871de99e21556ddba86feeeb79168a27ba4c7f46b716afd29c915ece35e192e47cdff65cea5c35dd963a7b9ba5232bfcd3e8240bf1278b36d6aa6dcf916c22e853761e388d3f1a40a433aa34b506bff60e623daf6dee09ccd69d2781bc4d993565b6370e7684d7aaeeb2b1fca601357cfc35f67331a5714552e9eb3dd6fbf2119bbbca64ea76c836020d19ec9a1f48e6e38d20777c1c4cb8531ba222faec847efc90b465e9c34a7001ae1aae1a1b5e613f47a7d2a35f2fcaeb1539fa3306807755ed92968758d518fb4475f80a0a3ca9d6a6143384cc6083082d311bc387ed1b500266acb41b8126dfd9c93e1b1fbc2e1f174be1d7761604d9964bf196b9ee0a8706f863e1822950769d6662051d0bfd2686444c417b5ceea329e2584442c5bb2f75f8e7c180fedc7a3a6dc21014bdfe5ba28573f6415f77fa9325bba1727fa321aef3c424a762f29b3bf455f51babf7c357a4e5ed8cff49b4eafd2746f73fa06b1d5ed4bd15999b64d6f3a7121d69a9229292044de7007ec9f717fdf09dbf3b367ef4a7a1baa46b10b4e16ea903e4edaa9011039efcee40df9f889a16f1d4f4a9a29abd4c5d587197dc1da1d8130ef6d7f55840a3d60e13734ce26b321d00aaab7088ae1779d826e99d92f54b0b80606aa57ab42c90e751dc5b716baff6a81bf135f4250f00aaa0df2c833761247a34288a2ea47bb0bc836a6fef419c37495bec860fb6529465724011b4e58017dd1415346d52b19101d5eab12b19a0e441575d47307cd7dece7b39f36fa26a012112fcc778b55fbd14f16cdbd3acf9a9169d544f15345cb6173467874f657f11a12cb8d9cd9c39420f84a35791fac6511b78d593d42fc2b8f5d872900954a406331151227f3b4ab61468947e78586055c0c09c0b98362664e5e1c8975eeaf02cbe3fa2404ac419609ca705c7393296997f81a9c8423adfd4de864c57a97922159f4fd88748365f7c0d07563e4d453417656c8833e29d6390b86fe4b794250a151fa247a5e1b9c58149170293ce42a65b9cdd8b38d9ce0ca032a2513a10b9aa13bd582849621d7b26e71960d6ab34544826f40f4772edbf65887cf9c90675a02a24cd768068be5feb2b8020cab1f939df742e78b85890a0ed8735180b8dac5a2cb32325a25e5a55c3dccdd36db1e560cf06d8edaff94c8f8793df454115ab6907e1433a2fb8a9710e410cb4ae29b749c5a56812e5d7f5071e3f281e22439f66040be430da6e3d00d79a926e6d938d9e102c8de762f46132cb877c9cd0719ef3124a78afd322abf925c981c80564d0990d6a8ce664e2e17c87b4a0f4d853162214634684c7c01135a65a96a2dd46dce177320faba9c4559efd833f3dd5086ba8112b30bbf4ac3c13f437f5c875730809567be74cf8a9c816f4fd6716a9b82a1c0305ed2a51e4878eac2537a240468d0bca71bb3db3dc44587370c898536d53d80368b46b639edce91842b66e208b95b7592b85d9d72657432787fe6037eb85211c1c8adab3ff2f751ea8fbe3a00085f306569ec17b74af594427ec4560689fb70b4e6ed89f6eb53a9e4512918249b9551e0ae6c9f36ea559e9e512e2689d090a17415450b584e5c7cca50b33fac3b1787034d0208acdad636fc3d616d98f1dda10eb9991d957f037ea435e3319c366ef658c47fba775755c948610e7a384fdb026d34b79b7c65045b283b693ec39a3a461580d5e888e2bac27ea2c619e06650b23ef7817b83eebcdf4c1046def9755d8aae19209f9ab2556c7c1836228399dbd063a417e004937c4e8f1238d59628623dfef098ace7e0e94d254f75cf60370991373e1aaacd754e553d42045943f1356cd63e1f3db516c660ea72208a93925c913478d2a2d2d9e8f1fadaae136ac42187cbeaf41a1fecca67b1d9f8e45b8a21509b73b3cda6895c83a1346812b5ffe2c1a73db6cbccc95409b09f062caa7414f2a7b9182faddea8857099a424d6bff704d75eeb4793e53fd6c6b392efd365d02217bb0a66eb65c8ea113ee6800c30e14140d1db32a753de462a44320d257b6c021f6b2dd25f7507d6320a391d0b0de1d495960f32f421d11d91c7ea58f1838f225e75d81035ec78aae5007a650551a2130d8380a88a42cc5f00243adb65118f62bae9123494c72cc94cd04a9a48393508a4042ff5d4bc0de75cf5827c32aece92426e6a4df489f63ffd696a5e09c4569596d59d11853d305eb433044f7c58f0208708aff7ec7dfba646904fab95e07c593ad93e76728526f8818fd7aa58402a80feee7eb15a2f0b3c4cdb007544c53de0e1f82c11bd9d37ec0647180f05a657c2933fc48c254c582ab7479a9c83bd7c4f8eb0e29efa3ed8c4095fe08a1b447db0a0b2c4929f11cc7d7829662940098723811cecc8af257c9ca591eff5e55f15c1ea2954f52b5693fe6643d48e9f2f23f2143777b0cd7c33e824b1df67327692829046a46469f24a35f7a4a268c8071d3b08d4de93efe35e71b2ae80a054ae6cd0345a6af2d87b666f3f5babd0cd2158f9f1d0454c258d701393f9c43f96de381042f38b49a73be70882662b7070df771bc438452d607e3b501f82092cacd524912d368f485ca72dcc9607f4b1db83a3bd6b6e02d235791457fb5526e68a78d321645077c96714c920ce0f10bfbe8a0fd9da0b9366ee0db2b686811ff10cb381d13e0536e73e9b49c2341023a2ceb8651403207aba6394c9fe86be75b1f3c92342221fafa5b188657ec49279ea611926609c5a0a8b137d58691968eec74595db30a5eab47f49335e4f7b0e1c82e6cff21317280e2cdc9c35446b64b3c2295b7ec14cda916f25e16626a75feeb120e95eebd9754a395186204d03d9241a401df77194d9938769c163b7eda901948cbeecdf3e841abf27b3b8c92903ee36dcc5d060953aff4f2442313a8191adb4c9b374dd9cc5f08e8ba058b5c03dd111f489e461c1723525286162fc0c4846dbc36eac9cb8d32c1a121ebf3d81ccb322277755a2e55761686f53936d703806a52065735bcf568ac9d9adf605be45a56b980d198260445b04842fa9fd7ff6d367f50ed8553de4d1743d992577279a8d76586f479110cf8e5875e0c4682626f6a1e55d9116fcc4bebe725bf5ff24704153d8d7b94", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a2c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851aefe0609e27b1f5e4ba46cd640bb7bbcf5d11024b93b2b80830d9827e59538f1b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4135a40b6144087f868125f387412d514b5747bfc6d2fe2bf59b31a94cba4d7b722725d5e5594a87fd3ee32e41daddbf89c75efb45a474670af2162a31702ad09" }, "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000040ab06fe21da21825000000000000000000000000000000000000000000000003d09f6ba158cb416a00000000000000000000000000000000000000000000000653ab0fe8456da7cb0000000000000000000000000000000000000000000000000000b0d51ec6f54400000000000000000000000000000000000000000000000971f9a84520aa31270000000000000000000000000000000000000000000000069a8682bd694d08990000000000000000000000000000000000000000000000004aac14ee1f1f9cc300000000000000000000000000000000000000000000000000023c403e139d5c000000000000000000000000000000000000000000000005840763058f90f862000000000000000000000000000000000000000000000001af162e6dd3d19c42000000000000000000000000000000000000000000000004606b05f7c092197c0000000000000000000000000000000000000000000000000002ec3674acffb5000000000000000000000000000000000000000000000000d53670d6246e537900000000000000000000000000000000000000000000000f962e8b1c78199d1300000000000000000000000000000000000000000000000a30b52d4914d59ed9000000000000000000000000000000000000000000000000000115258cf5acef2743e93d4e6374c24aef3bebcc2f4f2420e9ba7a5714ca96df52ec420a5812cc18aa6c4a565a8eae50ecf4b59ac47f274431bbfabc786534686220220083c6d426a4521a0e9e50e463baf8bba7d862eb979fee57112d10d6739cfbc5a6fd1f9512071ec987928cbd4fbaf9d86e277597b28f7f28b44afac1d78d050684ee7ab32ef47cc61e6570347351e7d14d514d6aaaa183d8f3136db330f81539a5ff03c021df9c271991b7567f82707a8ad58cc0f08a854d6c66f37ac04f4975ac8b16fa235b7da200d49e8945b4239de3bbf5257002bec0d02ca95619441e75985ca786043564090305898166319bc14b2ae8de7f23278e47910d4af9b22c1af6e4d6772bfd7385b9d88b321d75fb535fb79e9653cec5db75ad0ee5b430d3cf69f3f2e42a8c8cc6a439e517bcd51609fddca9af121f15696115028a34f01b99431f8d2d084c40dd7a68e2440be69d3c4cb4193013c9a52d743256ce32f3ae7ddb9966c82d4e413c9b46708028b32614768bf0ee52c354d61dd7ed7a6c07b4b2160877f005217f14563fcde0b9affa023d3ffc28acdf88fb52a0ab455edc3f200c41d78419495bf2c53688d2c38eb25f9b069fe0fac1156ab227d167346d68483944b5cd02d388139006df14da7dc59c66306bb63b141ffb44b153d3a2ce2be113a26fbd0f39043bbfa987f698498492d2dd5a9a107e44e8c797477789319d1c368f17d12e2ce36c2407c29941b527945fd15b8c65295e960b553088196c5c3efe8909610ac2ed3cfbfb0d8f6b42a55b7d96c1d6b876bb7eeaae3a52cb522e43842b6cf719af9dcd59ec0090750f2b640dfa3838c30b7fc60d524f21ec41e020f83136ab097d308477adc85664b412502e9e074c8ce86605f0bec32cdc0a92007bfacb4a0724afa887cb10b384a0f8a2f435e5a6a8a2476c1a43e794b5d040de6e17d96705a97dcc7110d434ef35baec043f75f16a8ad2a65ef85cbe11992a9cbb31614b1ff0f0f3ea28e6d81f4eb1d6f1370ba58e2534cb627378b8b69f6daacaace2fd158335faad367b8a5f88eacd5165afe29486713ca7d4d0a797d1b4a559d334dd13639843bb7819198343f00f242d17f223126fd1719b0817cf7fd5687b15fbd2031d179543cc51568d8451eb313243d02cab952c0817f78098276b027c46037f05df41d2b0c7bfb60da2364a6be4d4c6efc8a3bc3cfb91bb7800175e8a7049322469195f5e50994463f2ebd5690e6dd07daa9e797b5ef4cab2d13985f03f8994232deb337dd366766174a6024864c57858399fb5aa60ae7ffe0bc4f60558e5fc029d082fd155f55b3b7a1d5d11a9cc8d05db1bf23bf81781e0c95a310768386f136393e0465b8d7a920b5f46d99cfd947040ebf2edc4c9b56515fc197e7edb320d5ec04cff4e878dfda85a4d833aa29c3732cd973cb57044f72f28e220f086fb2394986b66cb4c5448a2e31fa30250ff6a017e4972a2178aaab76d92d14b59841bf47a8f314c8ed5cc73529ec625ee72879c6c70572cdfaa59a411146f53483803b645f74ae55e77770167dc9f8b9f88352d5d1405f2b2e1793f402ad14e0ea93040f6b5948c98694398711f39e7c5083d3a864d51f5d18ba87dc2604e5328931206b74e1af900f25a6159e24249b86e36c64edf56148d23e3e38eb6c415256d073fb0322559c292ab8be3d1bae23947cf6859296a20c33125166ea1e5db893200c783848aafa0b2076135f23aed7b2f6841c5d864e9ab0319b223b3df0dbff31c4ec54ef7cdce4533e6d6646188c6e79e0c62271353827e0cb821a320bedadf09e16bbcaeb45335a89d7dd4053c17ba82a9e0240b3fff9a7cd58af3f70f608d2e354669ea4f1ad977ad4ad13ababa66c1fc2794ee78c9fc4faaf890c9f2328018cf65cd2daff5027404b5baeed49e757db29c96fa3219ebae1a4a1c682c85131133f4d09ce60ca38a9cecc6a345a442cd11b6ed794e3bababd71f35c718c3b21a461b3893f154353b00e3e5c85832e6737f7100a9a5e29e421d31ff3fd1acfc177698f7e90ec5658b4e542ba9d4e202e46b6c576a1d694d7dc59c02236d052a098e5044c0f357415a36fce6e34465177fc0b914d8004ebb1ea7d7cfde7e91bd08cd0f8c745174106bea30b2acb002d58b77a984a681747c096470a038878b2619747a68514f726714f01143ba33c6067623d8d784e916716ff2da9c40f34787207b327f55b17bee72826ac64f91c43badf86e8117648079adc97e81059a5ed1283696ed822c0e5b0728ba6156a0636ad29ef805fd968b0efe04e72d174bc4dc05d38ab82149ca15c737f244b7c198deef75536eb116a6b70c99caf5a411978829028ab6b848af1a7bdf7240b46e78dfaa08f6e20a2ad9d72aefdb9b657184162d1ec631077462701554b4917f1598235d2fd2e728a6296e841f5109749dd4ac20add72426e1dd53e17d74de24f677e889c8dc52e462e9fb12989153424b619a168ef2fcbf4e23612d222a24d236a685e7ac1dee99e55a15ad5c5ccd1a90868c1f16d13b8af94831b9d998517f478b12843e79b2cf8b510e731df1e4e53d85f027ba9b775ee38e4722c1c50d840f864ad79d5b7f211bba5ace55624d9d67bf272750e7e77b265292e81e72b769ac39c69d6b87e0abf8a12905291bab45049bc9003580454d1f45cf3209f216a215f94e8fa0296039d960ca361184adaf05a5110ad6e799d393a4ec57ac1b496f0bd4c2d2d0a21704d2e807c02be4c0e65f71861ad74d91e1daae0ea9f7da1d529adb5387d14c79e9c88b2b0a5ea1354a2c451726d5453a94e0188988fcd6be18f9e0daaf1b3b4a4942c4ffe4bbe72a071ad2500f4019c931b7a61459490286f70ab69c358111ccb1062c984dca75e9be31d83f25a3f36ec88d2dfdba029caf2b502ac2d27ae6e2e4bc2c51310b577be3097b1904113b78cf1b719ea164112ef6f06d8203f3fa089bffa1864bfde3cb53dbc70d05f2887b68f9ab5de5336385f3970ac6601ca216e60bde4fb2727359a07121df2d366ccdfab360ef1f550cc116b3c4c12260c44d115662e16480f4653e71c601301100a4d931b185a9f7237ed93ffe377fb2c8a41ed83628be99e96edcf0ee871fc497dd70ba7658fb904f63552ce36737823d94cb2c86b5d043d00771798b0e137c6b475a99e2fa444f6249c5faded17fd2593e841741993be5e10a0ceb3fab13491b3cf515f2488e1ae936799cf3171105220015ed05efd0df77b02b685b850d4dc8e08ab1a1e4d84a89d55c2009f137b071b5ef47b817e8cfcfdfc318f7ee2e574cdd771653fd4ff74f23315ebec3e844c7fe91103cef4293ec6814eef68628463a2acefb7f863024e93710531118059348f66c509d87a173c331a959375d2d484ab1e6ff5834122e05bbbb8c5bcb5a3cc4f940e46e230bd2eecad42884042ae070003fdc0787eda93dbea86db1bc786f91b8124e8bcc48b53b91ad2743f316cdfb4f8af4e985375743c1ce93ddcf33243d2a785af13054b6f0a8e2d7bdce2c0f57dc1c3c616fa0cb441100f067dfdad8a704b9409c752b6a44a8192af35104d6a676ac93f8ab16780b7311c4d9e09ee52618cf47f75ddb4debfa560e325f13720280111945493a97974e573a977c4be5cd28073fc26df9e47a030a7b3cd60a9d160bbdbcf86a754657011e6221e8d3c0df4cc4f51233d4a587362f34660c209d0ce770afb06493a48ff0188e7b0f2bfdc3290970cfc07ab046a894581ca208af11ceaab5973914f6936e5e1bb5c7171e41a83758ddace922067cbda7bec528a70f53d8652e346f2811637a2bc348fa4a329543ee215284507e18213680c01b87d158aa750a942593d21665b070248eb05d9d99a3e718e83d051b80b4d0572cd3caacf4b04d9b02e7b5d119f825c51ff746f1b90690fcfff820812513341a1e518c70665d56c4fdd8026696ce962b244efd18337beb6d851371691ae548b0134e21cdd57a574975f136c4c9df4fd47242e4e8d5b4d4d5c64d434b7dc224f80ece578109ba28c79b93d58ca355122180703b48e1e65bffa0e4b8cd0834892a1af3698b86a6f19f95d7bb233b3da96574a99ae4be95be5c6a1ac80cecbaf4100cad09b438114a5de5e688f2350b6bb9628a8d529f64a0d03495a940b8af841219cbf4d848305edba7019532ed0abd817e4351dbf89985edd837bb84a253897f0f45cd9cb9704c95846fca3cbe168336a7033b9e5484e1ffae1377e5bc24ac8604d341ff988a69336a5227edf5f1ebfe4c03b8080006f1734bcedbd13c88998b04fd88933744df41cea43cf4364514c189bccdae52b745765d39cc7323e0808926e73ff470ae40e1274dfd6e4f1a1e43d34ba15f6cf124dd408631b349b3288d1bde5a64444aa6d4d969b48fb876fdab78c00be233cc6d7c2285b5d57e0d541a215591b9ba63f7a1031540a0fd2ef4b25c45401435283b8f5714a6593377fef92883a753b19056b11835d43a8dab798ae41ae19bddcc9b77e4a7d62a9044237d25debfe3113d1c32ceef3f28f255fcd7d7c1cc926ef05eb8fd38452db84907580238852d6123effb342ca4edc2e2e6cb1f3503ce645286483a24fa0d5a5203cc1901f6bf1750b0562f89622e7844fe4dc4df62f393e2df9ffc367d5559fa0eaa2e9d24c26cd530248c3612b7b92324fd60e9f2809a7714a432795348dc35e1b6157cf914b2e55748cff5db38d153c4bbfbbea5d69640d6ee322b2486d2ed27b206d734392617d64c10f7e920f8b861f2e2ff047f95f64234ab271cae6257fe270956d1443a7ffa4ed7934245c40547d4f1849536d1a325f89ab9c7068a918795136bc9fc6e672be03de71c3480bbbd5d86b9bf3271aea5ac41af91b48c52898c165691c3be42b9f43326f98e9240862a025bc5635dc1c606fc0f2509d1efef1b0fc08de20191f99e02b70875d3dab5664655fdd5290c12d141284f03b5760975043a240b864666ca80bd0d9834ba077440690d7e32958bb09128d192c4a882d80291a7bdc10b03ac9e1ba37e18351704ce1a60bc4cbdfe5396fe6973993ba8a0217669bd2f4ff5f684fdf64f00bd579194d190e52d3404eb217b7bdb07635a8704e5e9cc2eedfb4d5eac2c8c97f99a67a70b722e722545ae14a382fe970a42ce2fa136aa4acc0f44847caaec9e9b5894fce40b03f53e879fce904b96b2944c3219a345407d3b0b4701deee680e0407a6ba609db5af0239234e3038e2abe9573d1cddb19fa9e9f7cf586724b8ed082e215fdd0da169f6bfdee0c722e449c65f262b2aec80e8ee480b6410af0902bad7c53ee032e7835fe9dd4d3be4567195df882e777b9560db7442ad719eaac80f50ef874f2c54e279f3d54624ee02f47fa9501e264fe1f239017e8a52e0b02360126fcd71fcf44f9aef62d0eb47d6a2ae41e72ccdee6ac3d32aea7fd40afc96b019f5a9951a0b411e648fd40a41c1da06214401b6bd9f4b96db40cd2e3c3999220a789265f0d09426777713470380bc5bfc5e1fbbed783e3fec98db96fbd0ba69cd290c27907ef7b1982e7ef331bad447a1880722f5b7cb793fd8f27b9ed1d31dcae36e2338a2131a928c1d0e273db270b05e2f9b8b2e7e2762b41e67be8ce9aff152d20108397f3b73bcffa359b7f1ef0d4a2e963a4898c66d5706967babac451f8be3c3576e914f059af6ec9aa0952e5753019bd4cf18e64290f1c165856c17f0ba9651543a42a2ba95777c40f383d5d5311e747e15b6bbc7b2270975e0fa26e5a46291cc68074be35e4aff55b3e5ab75ea0b86348e389c8c5fd94f675becb39304de9b7aba1f5f24f9436542ac7270e91809379260ca1721fe623ed93c31cfc73290184e3fd5be777ff9748a62635aafce2400b92b1237ecf7292a17c5ac3b3d49ce2c996f9be7c74022ae9328269095f4261d23c6956f89f4d53b4dcab088be3fd495b11761c7738db37914e27d4ae9a02f40c24808c10c618690498fca7f59fb1d4ad4900940178b7cfd3c73d8a0b4a6299532a13a9c4aafe6663eedc2abb30f5dc5efd034730725391abd53c9b10d0c2e83c607da7e98c97acb9a1f579133f4cab156b61d5db4d14bdbeff7f42c65d419deb1cf3760699ead370c8b82510c0b32c579ed97d96a225c1ab0686fc6130a074eca7416092782b3271155c927f47020e8c0f1323f683cca2ed574050f733001814f79d7e5adab32768514f5804dad018d2ffc2eb9339ce639e4217b7cbd59290c6c55c4161c0fc95458c7b2caab39bd0ae622b1cd31a6facb5771fa91fdc61b8350d295d4a8ac29612ee0c7c58393f0fce2304cdd04e26b903b989a890d2512f3245c84b74f70e541548f1b43103ff12d4183ca15f6c5c4953149159f688a066190fb34a540cf37b4b011e5d56d702be708ac52a6381e6c7c14fd5e2390bf0f02054dd47b7c3130ba3b05a8358213791d14abd0942f197ff41acfd49e25000029febbf849756d5b0a1ea245c8e72a8ff5d8142e0dfbcb03d081a1e6aa523c0b9be309992df10251df3500bbefde5e398900b31a2873400259434818825128009b76de86ffbe9a22f6b0401cf2a1d31b9bc7a467edc49d08bad4a3be6a867e0369d0df4228f24157e3e74bbf98cc4fccb350cc0b1453985f35bd6d59eb3bbb0c8a708fab3e36564c0fbcf59b52b07d420c6c27b21dcbb404d78ea17ab65ab22a7ee682d495e473c0b1901da9986e35a66d58b90181b66c648ae8a057a0c65509d48727dae811979c6a30df9bb90dbb3835baf0439724bd6a16546f888067321026e81944747f1c22e9ae204cd1991984a23fcfd5310572b72d74248c91be8f1d39a8b44143e8e86c599d26faad8bdf1aaa2dfe0a5a6bbd420fb8cc4827abf30fe2a9d0839b22588fd2a0dc6cfb92fd6f86afd7f9058f6910f99e65a315a0c106958952ea6ca6135d6b09f6e67571bf4a4e4641f96965dd1706c572337152531aa504225fd8ef9d54705af04553d536d3dcfee38b3de834864252be74304ea8088fafea00475b0ad5d40f8ceab5961c9d626cafc84b26f84400357447687096293abc23a2e828c5c37c97c38f866911d9ca984a03bfa86b291bda99435500732ecc4388049288b6b777af9d4758126f2a552bcc16e75bf97bf596c0c497237f1c9f0b1ae3d97bc4531060d3ac576bc01cd9d5f10e137030e4bd33a81e16a8221e79ae12a6f7971c03de925cc87490f9b64ee9db4168f1b03d18f286ee8efbed28f9b5457b522f7446cdf3390c66ff86bc466884af706dec76517f9be11b1c4a24ad702d86399352c2f7ad293ae42d8eed275aa8ffb377b3c4f2d547deb234e62763ff0f3e58cbfdc39452771c14cbc6c8eb0e6911d53ceb304a20947766d8b415fdcd1099e3d5ae5021bc943de2cdc11ab57fa9b5a6f6b063ce886aa3eafc5d1a518044009fd94014486fc043c811bf0ef250d0ce1a073653c935db8219087c2840edcf89f08a32eef607cab2cb9140e18cc0cdcffcb023e0b26ac6691e290e01def66e7b0a31bb1110291cb92cbecbb0a5641fb9f175671ed9a114b757acc6169bebf9c9bef1c27372d5509192f4dbc0999b57d6501f2f4e04bf5be97bfb33138ccf7f178db34be5d750cd2bfd8a2375ee0ac34b4410896ad8875efb8d81131f2d5c591e39c8adab09adb775f559b695f390e934af08c8182757cf340407450bf3ab02aee67a410cff649e08fb935e1f6ef6556706d6f02464125ca1c6eb8a2db6877fb4822ab722f3b8e9bf03be6078fd699fb51125971b53a39616de119605075a795d83199b15c0d103f0c91f11395a517d1c83340c2c91aa42663d1a3e071e4414d800496e2ee7d8551acb3426677077be3c35d7f41c69d4bba4606ef7036ae83ef1b1c06905b50b2fa351b94d4be937016f6d8533368481481ca6fda30304474257de875ef5168fc4565468471c81b4c65719d4037d7cc7c835ccd80c01de4d6a992f902e74fe1b3195f1ddb53a672b331133175443811b957c1f68090dc846cc9b717234f1aae65a03dc471461efcf21e69d8be21937dca5aca848c90b98782989e7ad92e85b4f3ae502fed8f71ae2286d1336ac0d32b8a33de55273139cc206d8ffdbfba5df914eba7251435de570b239374f98276811d3469e45771db82c1754c29ac6c6e39643b34feda8dc704facf77b1eff5ef2fb8e7aa71def11a5401416d16223009f6de585be285013d644bb03ee8d9e124ff9ecd139914a2d37d47a97ef260781595c5a33bc089bf5b870b31c2cbb6e2cb96551d08fe1f100f2311a43fcca3ae43aff407034fb1b3ebd7b2a26127be42820fa97cfdb8fa02f41c7e12202331d288ff905e07293cde5fb1b4416853bb5c52bcc2120a445072554185db5bbc6c13c023e10e818333b3ea63907bdcf0275688f4480a8727b2f183dbf9242fb2dbd3320d2196863f6fb29fda90d4d090bb3afba4a59b2c68a5516cde31823fcc0d15ffd5476a12e54eb6c38b28d04a9fc2bb41e13ad2f1f2875138bed8a04299e7c714f7413e5f053e37e304248e7d969a4f994d15410f15c2b1d2aa31f2d70042e1d242c15c593a7a846a95fc96ba029425aea988212bf900e0e9b2a97f5f8b3549fed8148d271db03cf878b601a6e694024aefcc8ed72093f03a135b5fc9b72df85fab4825c6352711b7a71fc9876d66ffa5949500a6508c52518966b85d42ab90240e68488817e62085f222850ee38137f65ef5173eb9d341f47eba397a1ba71a4509aaa73f535b5e2a771a99a080ac81c2632a3096b287623a2779126dafcc6577a643972a6922fd3eb24bd3281dfb6dc1d19cd224a269f013a2861c8b8fc8036d89c1689e27a68ff32162685f45ee516f55279ed6b32372a3ca326977ec6b34cfb295fbc099ed807c2284bf81a78546681214ab14c4edf113efb51ddbbb7189ce15790f96f54615db145c0ce9e5e818a59e0ca7130a6921b9ae2514327086240dbee82447f852fbd5e599af80db39784af850b88d9060026e9833fa6c872b2c1372f93099606837851a1a168c48145d9951640a97b72092042dee789eaeec2724b826000d39dbf75ba4710212bfa31068bec37ddc16a64050095288cf1d25c9f9489012d3349074eec75085803491ebd8298924143273d1c498547e97386e641295ec8431522d17cb9dfbd2f2b4217cd83c808c7c5f18d10737bcd0f8950e8b2555b78e3f78e91834a37af5386fdae725e3f20bb54030e189b7db9b6e8ec5eff63867cba626e652be6c46d665db454445f0f83f08084af2113e5da93928627ef87936d16a7000a0a094a5002c5735caed7f84e96fc8af1114de25f98cadbda11d3a91d4d182424c842fddb7c69cf66ddf1ecf33311c9e91b198867975fea5bdccc98d7e30e8f3c8d8a12e2cf1da8a91da8373491c658ca0ac6e1bca3d1a63e41cbcb27336f3072f08b53d48c7ab0d0e077e2aaaf1ecc0e1c3898c8633a490bbed7d87f0b648dc8f0f18c049f79f8510814b0513f9716542796fad753e5425f502639f66f85280d9a026609045e5ce3d85d58cddfa0760f2da4146c8424d471d5bd5992a8fbc67b3bc24a107b83575ea4ec667c8684d4a50d879310cf292b4176fd2da386d2827c6a949c75f45c1af3841c6742811f4e532b67908e68172683560e3ba9deff96afa53543c7525ab1e2572c675585600a3c0e8d9cccbdf1d2dba272332cfbf5dcdbf4598ca883d526f0348b407a0b53b7f72d63519b8a3fd6b14bf1bbe4d0caea0eb71f6303aeaf5198f0b96c540561c7b024593b5c3bc10bdef4eeb418bd7d40e811b46a10d85eb1d03201b1174418ebef2f8d48c74be847aaad4d92bb1c1a4905fb0fad9118f778ded6e9f18e291e974118d6dfb276396599d87ac39e61fbbd9acd173234149be49b533d3329a6a471a00d06013725130cab4546ae6063008eb76f952a8f7b1d4bc364948346145a1d512950eb1ba9ddff18f9a5a6b46dd0375ce99fa87e59b41ca52fa1956a5f5b54851cada72169d4dce1449ab2efb0c6b9ac9e152462a4b94938d0e02d656b2b302516047fd71cfbc6fc5f71c30e004e4e77fab46d560bb462bdb52508506099fbda2a132413027e013ca6de54dde3497cd6f4e165855c4daf55a23da099c2ab11422b7037249598689857c3181330d7c7c0c85723bebf2fd38ab79ff15ab71c736e070cce892a0e995f3eaeeafa42934a50cd1c3f68cf168c50c2541e7722e08b5a1952dc2def02fc665ba4ea42f9820ec05e6f622ac1445b710685b911d67280240ab866edeab9a93f511c74e06d9409f43ea822de953e04b405e4e258f3cb641e1d51e5848cf19ba0090c881d58acc9d09c8122626a807299cd251dea77bfdc6c1db5a608d1d7f90441ac3da7a850b39d37ba055d46822b7f4a440b369ee3dbfe0e2e4afb5a995b21b025423a3421ea55f22a69812efe31e835d00ffbaa26115c1e6fcc35e3bc1ecaa89b717d2acd42f0ace83fb1e88c08230658c64331a0d3c002205b52ce6a9a3afcfdb7fd07f0ff917b0682a827e6be0e519de443bd7855c028ec2309d0e2f5f228b984c5874b5c1ad10d7bd97a10bac7b9828e46ed4b5a281205f36b20d02c74c43cb5a415a3785ca8bfb4d3d4d2ec5816901120511ac4f82ddf777d020409db617ffc7d064710afe1af7171ab23eb3a2827242a81ac15a00c068ef21b34310cc14da591ad18e6e044f3eaace0ee07bd05ecbf722a7b6c292bc6f2d578c6d7d9c998f997dfc03a913dde3e1d965b17f32593a5a0627e0c092c10fda42bc30f3ce904e4c26eff37953802d60afce5cbb470bb6d15412e268f3023a4042bc161e38fd8e621f9d459f3effe4110dc22da5fa1868bd3918cf7930d18599d713f0b6c205283389b0f2453340975f771defb2b5e31c3857dc90fd92daf36046a3cb64afcd9050f4bbb9fa89428624a066a44dc8693c560b09ba22b0a418603118fbf9d862084533831534afd2aaec3cb75aaaf22b396d7188bfb0e0b3709fb4ce0a08b7dd002572a13ec48a5a480dbaee3570d1fce629dc5d0e56b21cffbd42d18cd9b62dd78c23ffc8d2f5ae1c467b80afb45c687c9dc954e8f321b43ddb100a18f3af3880a51969626dc3e15ba3bc4980e171184ee3bd89dee052639becb1659d8c12e42e0652b6a33d148999ce6153d4f4e7afc0e640bb231ca2a59dd79ce91efcc51caf5495bf45e00df2beb4120e7d30d2f5a0446206fb6e3196ba3c835908bd1a2bc0754ab6bc4819f2f5210fa494f76792070e5e2c454be02b1da704e87aa8489ddb9340f8841d94446eb311f21bd78713ae2498aaae1631834d8fede4f068b2a340d75f9e37b796a0d014d265179ae4f24c0e79811981d2ef1b18f66869e914aef1780d5bd351e58eee8423b18cc9314079f6aa2f344601c8b4a05c7631d77da06ced0416667ba392875d9af73a3157ef63673783dc13826a144f350975fa8223473bce2d7337890bd3cf3e56eccdaa2928923b06eb9f5178761d6ee4c5c09d2c2970c06cff0a9baab2d0718f5db3f61455817fe60a7862f8573382c9e2d1eb09bdd03bb2af0db6344c045fb86e12579a8205b460b1c0312cf88883ca725d6b7319535c10442a31afbb47781e7b486a30e4c35a016770c23790213fc854527c7dba54a75a9de6b88d741433c32774d547ef5e9d86d1d76125a9c41bfe6c1c5414b4954c5ddd09c8830eb22edfa6888f76e6f227358ce321c8233821ebcf0fd330a038028d95c8555be7bfe7e30a6e9a95934ca77461df027d3e0d2c5716339668a42412bbbc5e10dd51fe22a7ab706d88361503c99d4ef0b15a392a838b5c3b67c251972cd268b60c15d3fbbd6bebd225ee236d219d942076f9d9bbfb77981cbce154b5b9b55a15e74416b9af9b1dccc957bafc744c124277d650e13f2839ee6faa36b0a3b4db8597de30e4b95efba2d6aae287df1c67b1eac34ac25df0fa2360d47b727bf22d4cff0745872825394be099fe22b57a12614ed7350f7e16da6c81c07dfdec5c33efe7b6486e06b96a4659ec6fd8671f6a82c55903d7c139842b424d91666571c68928cea428bc1d612d6e7f44bf7c27d0f17ed583db00056b3382cf91233026547ebe5f93f1aad6309cfc2e419a7c179520e99025f7f20d9951f7b11b0319944d7d18c1636bcfea5f034be45a58632372204770cb1ac257d09f1a6a07837916260885ea279463d727b331b264b1f0b49381f7f2e77f8cc6a69171a6a7ad635e415ca81bf32489b92644e873a4fc170ffe1117c1dfb8f1d10c543a1d9047564d769469816930e3f41854864298374980cde254352efbe0abd397db0fd78222d71595961d9981c996d5ef96383dbbb766b840876fa3e4dbfee7486a0199e40a93774fcc202ca7e094b1bb99bb5c03d5128a626564e425bac7f3eac2991821756e078e406f16d5fa573f59897d0d345244d590b3d34d64df3afc81bacfd30b9e8164ed6e043ec5250231e37f9a6a8f652bd7f0fbaf17b908d410d6d947d7fca1d3b51ace99f755928d9aeea46034d972a296d25beb92b01ebe662acc722dc0eb9b8ad40227e67acc989bcc820773e1c83aa612905aefce55491f1925af1c04f0f231be3514324a179e5315a1bb37dae95a5661f956272bb735d7b95e89c33e7c01be2b7e83582ae1a355e9df4ecc315e84dc62ab2c1206457dc7ce8ec99f837075700053651b11109c8627bbc53705b593bd91c3da4df434d327d486cd25a916b8607d33f31ec6b9e9bff15eed344c0fe88280d877a5b4bcdb00799f7837455cd919fddd991bfdd02dda98cfc21db8b9b880e0fe664cd7591257dcc7cc3e0c131721b88d6fc684e93073ba5d5b050eba773ee1f1917ab154ba311a46e1de3a8818934b0984ceb63d6f8a0e8fdd1e88f8ccb8521fe6811cbc580e23c1ffea1b329d7e51865a34d1c0fb717e870a221cdf9390a1815630d082b4327aaaa31d4e2c343fe24db2ee45cfb6209e7b3f052f65ea94a05769a31ac312b65e8b702576524d2e51da83c3511a1b211e67cf750a2e2a63a003f5e68ab24af990d658fa911e9b16d48e2dd4c39a5420208eab00611a2952c14a3adf3a46fd71d7dff35d4a329ae6edf88bf82ce63767a264c0f55f58152e12f9215f4f60bcf23a87a052d4ff98d4ea15397227ef1a31e27630c2598afeb781697c705ff91abd6ef567672b21af372848a5b907b137380b6f35578fa10c0442f8e8a9c1185d5390101051a7bcb9a9cc26e54dfc06dce51a827d771e712c4e726a462eb20b0b67b110f61c511ac1d1768c17bb76bd94f4687e926e91d8d326a169341d5a6c63d0f4d2a4657014e2d32ef9e2605e83ffff451efd4e44c31e4331cd7e76c07fe587393bd2896c20f496a1284126981e6dd39c9a93489dea6aad722589bcf9152050f6fc89e1c9bdbd1cd00b14c92dac4b0888dce9deb64d34b6e2799104440d4688bdbdf16aa80a0032eae6ecf4f5457dc7ec502f9d6541e57f11d0a33df49cb75b224d68297662a24fcad6c6c4a1d058b66b52e5194be351854214297bd50145c5117c2046294d80ab362f32cefafe752aebb35e18b6343202b088a5e3740209e74a2dfe097771d91abbf91821bd6d8d2775370bfe80c3b05a20b28e7e4a875a7f7fcfb34543326cca4cf25a9711579795fe1cc240533bb43bc25531a47002615f2a582ae9b97af5ab449ee49ebf1524fc70284c5db9d9ab8d60cd6a756b5b14120ec32650a922f09b9ff0e05191d9cb64e8a49bc9ba9ff38862f8438c9f72d8de501a998b72b3e094cee71964c093774e6cde95231bc7488af29ef82fc95f20b73e22cf7397dc2f815c98cc0ab35ce3aa2573b3bfa52c1c4e20b14ce9085edd1ba3efd5b4e7eae79db51e0c0b6eccc2d8c2fe017b1e953050b10865d5f69252f27099e8607385efc6b34356105e0d55711c256623bf827cd6601ebdd0093ccf8b5b7c22a4a7c023a8f3f6f9c2e033af6e983488fe17ef544b70afbb9a6c7baf14c4c66d617464ae680959258f3d6d0921d8220bfcdf54a201307980f3e5d8997783a5cc6260d02bef3906564b64e944d925cb366b68aeae3762bfb562400014dcb231e337e4cb1835d935a6ceef1cb72c2bf7ac05b9b7063b82b65f37d1d1f7836b8339d8b2588c630d786c8a0cf090161b1d37814363803581fa5ce82abb747da6d6c34a0260e627bebd1d4776e9e328fafb6d1383fb9b2062ee54c659b246a40a81b5eb1901216be06a9a6da8aa2fcba770d5ce232f8bb551259bb0dcab25477febc5d2db3124082c3fa025144e7ff3a2f56d6ba87a7d2df10fe1067d51a1356857e5a675b99f2573747dc685678c1896d6d820d5fbb6a142365320332eba438ef44aa656caa7815f18b3f9c59eb23e8b60deff463beccff1f48996cee11a6a538526d034a9b60f065fe0f5039bf073640b2ec41db6fbac5193ea88028976c924952c5225f55aa1affac5e7586245ba59dd8a2df0bc13af700e67b6fd740d52b6bec53ad840a94f31169d6dadbe51fc56a45b3fb95b7363e06027ec79d84a0751501e4ce5afbfd2be1117af68a171593a2ae7f2517eeaf3d0a26ef542845f9179278f756a8155454f79686846fc0527bbd8cd9f42ef1287e06eb851df7c68cb72bc28b11fb26f5a0302f4d1431d9bfde843ef7c022000d9322e65464fc3fd94083d6b1d693fc9bcd311e078cc6fb4bf837c86b1336509b0c03785ca3629140210823f3a34e477e6b53113a069203acc49a6df56466be294f", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000118d15dee8fe79d31fb06deede15a25b00000000000000000000000000000000e022c379cf6912e7284c19b56251117001cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000204570926fe529f24875d8a89533176a49ffca8b4ec77e09f906026173bad3a5b10b0349389bb71780e445df10f62d09767cb1f33adb9e70f3c80e993b3a182a701de686c5890ec62461ba8015508bb7418ef03cd6e8233628e723989def87dee04ad5f44f7518e7ecac47c6efae1c381ded114664038eafb7d13d3449f625811000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000a6a7b38193b9791f900000000000000000000000000000000000000000000000938adfed18caae40000000000000000000000000000000000000000000000000e21f5e071314e88420000000000000000000000000000000000000000000000000001dcc3d8161f47000000000000000000000000000000000000000000000002e46c7ed7ae9e45950000000000000000000000000000000000000000000000006b452c19c9c3a95200000000000000000000000000000000000000000000000f937207f5e9d7f27200000000000000000000000000000000000000000000000000011234ce13989700000000000000000000000000000000000000000000000bc917c80a1df8a9de000000000000000000000000000000000000000000000004158d1dc6c5fe0793000000000000000000000000000000000000000000000004df57309713331b9200000000000000000000000000000000000000000000000000007698a06035d700000000000000000000000000000000000000000000000b5363c615bf3049790000000000000000000000000000000000000000000000084836ce0c2626f54700000000000000000000000000000000000000000000000de7f5f0f37ffeb22c0000000000000000000000000000000000000000000000000001bca3c66a8927213f200493e130f2f241df6f50e717977080ee976b404ea52ce9a1739a6538942186c4ce5d408bc4469b54741acb17f81532bb104e167f13788ca258657e19ad18bdeaeabf611459f9516a80aa25dae22a716359feb8721e6f73ce82c65ed90504097a64060c9b98a318d0db5087034f8fb8be44fa54632799b74281b02fa3201b5874dca01aa6c15157bbcebfe698062bc743018cbf4c1838fd88dbc7725f1e2cc9baf93dc954f7630b60d425d36ef81eb1be7d8436127e0444eeea525630a31058031e3a95f311de89e96eed8d8edd125cde39fe180ff58ece8d79e1d4b19f2d6403f1677a3e0f98f7d8669a44ee9d1c5227bc6dc8f7fab18b5ea38a23a8a909d6b647f178c12bf5765e4f3fd9e9353f1578914f6289ab3626968195e2204a0b75d68cafcb752224084dafa2b4ac94248907a3cf380066780a921c968cac8c1745343af4f66eb1673679b9c00300c26624904c97f542932abd632e9d9c20b613f65677439219bc9826056b26cd285c1f6ad8cf48940c417ae82c3b6dda3ef601d957cb411fdb0e5a236ac0758e0f106e5c2aea4d997e4e082051f36f37a07e193e06fcb0040e75764cfe7d7b19379e865f44cde8c4021fe7a2cecf7276e482125b04ce7829f541ae0f786317799e17a5d19b18c35032546d862d824f01aa2b2ec87c35f2af31bba08cf65076c41c5b2a532fce570414bfc6619bde14f1f90519c766248124f4e391908fe40e4f4c2e47d959872db00ccc6c66104be6e3a4a01509488b91bdae4548075814bb3cda9893c4acada580aa2c29959515a57de7a31cc4f2c4335d29fdf50154c3993f6ae9031d06f0541f0e718afaf53d83631efd232f3df1898a0799134d1f263b0bfd6dccf19c72088d8811c5e361ba2efb54be181628501e2f6edc49dd9c2140408a2fa36d107456bc9ca11d66c1e426b5c3300cd87deaf468a61d8cd99c96247bf074714476fdffb493aaaf976d4dd235e99b154e79b2ff5e50ab7f8c6ed0796649601b9c5b5efbb5fc5f5b4f216469ba83161e04612689209e87667d0a819663d69a261da2a789ec0318543355b471178ee51394b1536f2652a7fd5ae364f01c53350a79a0a2c030636f3c2b0b1a4f11f8f70b7dd2a2ab687739334dfa9d83fc290414ae7a1369b7b416834d3971e6e4d10c1e739ee4f5603547ba8ba72746f349ac646d2ab09e4faa5ab7e68fd55609c6fe055052cb8a99d490c94cdd0e20fda8125a7fc867867440b977632d440bc72ddf2a79774bb4346a8defd31709e0d3be983bce16ec2f1b6eb0d064df61d33fe4eb0c0a9a31f221442947a6eb27a710db271537ecb73fcaa4ddc4435b85207362ff13ebd5a7cea692e2c11170b8f0e19c3996c9d4fe03f579bee10705ba2cb6a64120e3ec3fd8a3623c614a7233e19fd586b3dcddb5ed038a7b87085bc0d01f35ac0a910d04789ce430f092f249b8a95e3f3e9e57d464b7e39b11b0b2ce0511a53f06732a3e7d8ef4a98f1c0197fe86514ff68a832af68a50812a33c5b1b3d7357f2f0fcac439af16b2c3c51ccc318db8bfedef50919ca1df0ecef4cc3a1295d63a16e24b4427e5397d2aadf907e4021dc59573232a6851a4772bd0d7a9511e5e940c45c13faf58cea6eeddbb0a5bab2b269c8d205cd66a41192cb1a02c691451c60cd43ed39782c235f5b7222db00e52277c1bde022d68a3dbef6836899945390002967f653a03db955a5c8ce104f9bded599366c20d9e4e10815d1676c7de75591876c3b768ca0c50d93bbf03fbebd73298b550e6731cc9df74ce6b043d391fe501dcac5bfba80aea4039869994a026ae689f9fd7bbc45b9da35927b07a1400b8050faed5f52a92daf28e2f7a1e113694c5751fa2819d425c7038381219f561d31ab37ed6c11585362715d889de16fa9c110bafe87a89fb3437d64c75602257e3111a8aae71085083c00061af68a50a444442c811ca0702b1ba59efa80707a12e0574243f7c35ad11d0499bf94e73ddb1a0551fd5dc7137b31c2cb4ebc6845b5c11c03ef720b358f0b3417c3073e3d486c03aebb7b020db1ea2ebd23e08aa22bd2657df69bdd20d3023feef2bd7b0efde8763523aae1b31d1bebc86a1577122722af7b9eb04d62bb5d2dd340cb78eaedc412be64449d0314cfaf30771d59b4913156f4b94b14bf9ce19924e16e516ff1bc66e6955224645525d4eb36650422744014aeed42b2f48def893f4b59685a7a00fd42ae1856463b2ee65aa8f09c21def2f6d4a920159b6487b8928519e70a5adaf40e615c04e7b549c276109ed3888e808c5f4a847d257ea6478a544aab60e354c2cd9251e4456ba225db340d3c35b841ab4876ba9361592e0e2f351f1cef7a81e5875c24f4deb7bd8720b702b4d02941825cdc9b2fd2f8aa1e13b760f9e5f6b87dc45dd74defd1cb013a8894bfd211818a2926b858bb7f334bded04473e5c398fe186bbfa744b7c667066c72db7d9a32d8649606e140be47dbd386381b1fbb1a6238a8c38d9346a624d27d057ca8ef71e68e91fdcd8345fca7dfa89faeebce29c454669b769baaaba0fc712dc541ef8280fe2e79458e28de1f0312d7a3dde0572fe68bbf86f046faa8c2e07927b080f28ec7285880a3c089913308ed80d9c983352b34317079dc36f024c88cbe698f1199da4d50f7601e1a92ce7e958b907c3d08ec1c966d10f838864b06cb903a26f183dff4905cb4bd6861c48fc2e538b3fa22385197567809a7167ec25251e71811b35520a843903bea42f31b157ee343aa5335adec9bcd3120a6d1e4edb158539292537016521f1e1719774bc3472bf8cea73aec615aac019b47e7d879642f462263b44a7a876820f0117f968f8b0bcd1478699159b190360eb331181a2c04be40bb9562ccf1693476d7beea398beed58780d39123f31c1c401b3e59502899dcb070a2498fdee2da8d6266a91ed4ff61d016eaac361e643de776750e0b5e0675313820988967945cf62b69b46bb5aa6914de5556c053dd102f5065ef07c881fcc2858d7069c1c6f7915d99118c1d498f3232b2fbe53f19ae68faf49837c761efd1110cb2769cf48c492062503b64b03bb339905c4871ba162089ecb07e88f922b23a72ef676abd989368ca5ca6efaca4a3546caffe57f71aa15550539f41efe8523d17d0a9e67ea0a1f0e79adda4f7729011eeba0d13f56e2e03db3aa4ea0f1b62ac55a22371b0798281faa7e0ebffa3f57ef9db292b0a30d64528ff64c693dda2a3524a0f84f2bb33871e969321e34c0af763e3076e7f460e92e6cc346eb967e087fe895a6acbbca4f7dd6b474ce3421a5c24f07129f17b389059945b911b318113acae2204d6152a672ffe6be7699486fb5feb674a9fa1d63454eb801e0b0071570f519abe7972bf439e8cf28cce87ba571e359329415b61dfd9605d2e5ee440603623c89bda7c629b4a2dba72f964bf3fb0a45d364198059f2bd2b10ffa4bb0a422889998c100754be16415e5a16450ec06bad480226610e78379eb5be4c2f04a91545a58c5916584ce39798d9f1dcf7742481e3fc90226b8a508e45a3179b207ca949a9e68369a4e7a0e259d4706e083f51f4172b24d00940a5e40c5041721c4e2ba1ee11f1bcf9c2ba0876dcd5bc3b74e779fe4fb8318c7406d47ba5b7da0d76ef4979ba783d83ebf8c7d834f6b0e244320263afd46395579b9a73d00a400646144e651a725f8d554dd3612862c70c98fac4a6dc4e7648ae1c99498221430bb83c7a9e1667315b09ff19d292693ca49c81463918d0c8f89e56e7f1cb588f12100b03aae55f7fbf686c6d8fcdfd2ec95fbde59c33ef6e9162c39088034e3427bfb892d72474b03a197db7242e68fce96b631564d2f94eed762c1c722e38040e22909f91577a6ec1e2c330e5a47c8bb5e6ef018f1a83388c73eb754ff32cfb0c4d38ba8b27885ddadf3f520c796d6fc499fcbc2a3c5a83b97cb77a5174d60d2c1234826405656d94521ee61b88a233af7754bd82be24ee44bef1648438d4fe075b2767664640965dd21e2f989bbf4f2f69c5b1d0d1ca61c90f1ccf62a2f766251a5ee4b01447113c913372d0d49d6415b7a479dc7ef064c184b9140d8bee6a0c1089dd993d092a9e654da9b9d13748d9ff552c1d284a9543151223275818ac1bd3d204262544f9b536b40ac64089e0ffe99370db2f5a5303b14f7f1b43feb901c4dbe086dbbdb2f83982b5a415dbd75c8eb212760574f15b9065a39654e17829f0ed29bce220f0eb2ba77754697a8babd74c5dc104896f4bef310d99a77532076a118a429672a66d8dd32e72a259623f4a330291c23a0b39b4376abd57c6b92ef5e2faffdcb6fdf0419185e0528d35366a1b865dc91dc4a1e228379b219eb52a47d3160a98e6bef560957039f8687bff28bbd1654b0e1a2290f335c53f0e0016dcb8f0a0c2e0d7bbccd7415da0fd8725055b26393b87f071918eda8414cbfc13d7d1c5beffe174876dd3ddae96fa6cdf87acad3ba1709dea8c80f4c7d2e4bf0e894e78471fa89f727f99c2073b57ad0ab2b23ab429995f9eb947fdb176e28c2ff432eba5830407d95888ac9843beee5f7c224ecc1a40afaf7534b5c3010aa00d026e74c47892b4c803ccba015b1341b380f9b441eb79f26c0b1c8447d23f912dfb9d2fe28a5cefa496ff3dbde6fc9fa6767339c8035ca97086d6aadb88b0dc1321688e887d98b25cd01819655e9b1432200c665a0a21d40e7e0244f1c373a202aecf5ee088442b4fd2c5313001a02f584d75eb7b6e52776bcfe474f016bd692a2062da16361e2626c90db4aee731753bac3d68a2963f02fab8f43d4fbf52461e1737999975f52f29533837f361e89088c3be55ca975278143f7bc986da19f71c483e99e599ac5ec9c532fbd2c9dab0e088b6978ed7e65dc278a736e5d03792040ddeb18cea5bb841cca78a2f449e0b360ab2ce1f1dd4c8cb39ea12679615111308ccef9632654f64e19d97906c95bebb106b2700a865a5165b9b8ff470a3b72fb6a7f3a91f58d47828e155cc94dcdd10ad68ba207859b696ccd40783fd81f814647a611ec4c8370bf8c01b1ecc618a945f5432f2981b1c4b34c75077d83d252d91e6be4bafe7db360df60b17cdc52871f581610fd0a35442b466230b85830d294f38eee8d3f464f1191bf6f3697840817fab5682593db32bd18c3e35c279a623cd32c6a3098f4ebd0c1b8944809175090b273ca8fcfc6b2381835cfef50ad7135204c539b5ef18e1256f2107d81daa0665d4e3bf06f0ecfc0a1cf514d0aab105a9eb9f695af73b3ba0df3cb2d1c46b7c02bf108567e6d01cf49e6d767d078d0f7eb40d460e34d78692e0114c3f260886fcc7e00c8b3c7008ff429d1a53aa9c2e2a41d1cb34e154057c4686b4ad1d704d5fdca331c9f8168d21ee75a74a465224f3327845653d52575e720a797cda03fef68668ce7623ef7cc9034bee94b8b0181f2b11ce4e8710e7497246d272de79509f4e2fe6c8b42422f866c5c69f6fd41d41c9c326912a2fc919382d4e3d414ef9fc9ddf546ab52c8d93afc585b420390737f26228865603a7ae33fc8e19c944994028eb2a20b57bfe1a2f15f0f372fd14ef2c297e78695908b0d6cedf144040f14908d59b7ffa58b1b1930bd65270942bd8043e38037bb5213ccccb00db3df8a661e6fd31d165ae465810350f8182320df72bd876d111321de34fe704b96862982f670d697336dcd782eee7b90ebac50dd31b62a91af03d377532a157b4262a7d1cba57fa045d75bda66dfc194c0b8d09ef91d887469dafcae262b0ce0c2d8876e189885ee793cc622d42f5719247341ca89e1cdd4026584e5087558fa70f750cb3e58f15f4655cb2aa5c2072ab82f11dd0b47a22d769197a373b5a799afb067f275a5477a2592432165fbe5e5fe93416f0816a98599b5cdb93b09175bb78f688bfa15eef313efb9d49047188736c9f096d86d047d951f18de0d5bb2b435d9cfaa88521fa55553ac1171aa3676e41ef1387921e9df4c7c6e996e54033a9115e1159e9cedc6121ec9e81a1f516dcde852f17605c68e6a41ce312832b731dee504377e669986a4438985af9523357a9f41ab6b9d08bd5e05aa971a009802ce32e31c29228a1f314a2da664067dae295662923d7715c95afb00e860d12d7b168ae050cf29e4bf1ca4fd61b9129794383fc25db22ad3229fe1d00f4761e37e897caa298944185e893a6bceeb54e22b467102f2d8e3e1b2aa2658687b4f52d165f059f4053284f6f3797ed016a1cd51d6ccd1920f19badf516351bb00b5ac3bf3f00da9600778734dfe59ff118b80c46662518e010c4e1220cc9a38032e0cfabd5a5a5da75a1a827179c567443a9f95c14710c03c74cfaf3ba71459a2e6c84aaa0c8dee4d69514795029ea15228a4696961201437035923818557408cdb632f2206a5d9b67d82fb00e9c7c62a3a47c330f5f0875884e7471a7c2cf9b2ed4d9573c90e0ddd52b583e9ef1b23884e89a0c0c801599f00ff79b96f561378e455584cde3a3f5a4c84a86cb62c0998f4f6beba9ff1d9cc5d092a623abf9cd026ca12b2c816c0a63ba7666422d2609f8896501f94116a4ffe86b44b683ba8071cdd9b7db1f7aa2a76e30c1607e8a6aa03a5384ed982b6b6e809ead534b62fd2963a7027800b08e53c94f16a1b00b753322db877ab20fd39eb60b9280409a4e963c6e5ef6f66e7f8713a7690762ad71158a9c3698d42fce32038142ee603c46b0d6510f279e56c2b05d7096c2416cc3dfba58fdc0c50a8b5d89048bef8ad00d7b6fc744234baefdbc29faf5e4694f0d0bc87ea2397a0de33752094611077ec049e3deae0062a3d54d9c55d2986f356ee54d7ec2645c18012c3dfccee4fed0bbf219d4cdd93b94c2f1dcdd964a7ccba4954033a07815248455ea2bc5b096f90cb81d4b534a82a8c55f8df025430884c395c2992128871e87c67a24158e7d9f2e700b685d9450e5084256931384cb517ce80ecbd955671b13c2855124079a3f8a423b00e0512ba3ee812ac85cc596ff81ccfa7c31dea015cfeea3704b8289741c066bf18d60a8f6e453921513d10a7494b7a8ed8bf4430e906fe2dca5b1f2daf50b5151e78872f7f971fde706bfc267af4d6c71c43c461b331c16f81c053dfc33bbe0973f91e979d96622bb2805eca8bd1ca1c86a7e4f2222e04ad38f0cee9892e7f9aec41ea9fa52334b246d476203f7c2d5f6575b7d126f845c8396208049ac014e067a49c7da865ae3e17c4b4630e63630abc4958e1924a21d32c26166f01ae2b8da72d7a56e1afa6621287663eb5d2983187964f81753ae5213daad6800140e34f3af2d7e227cd2c5bd9d4758586f6a4d60cd0cc70034d64e1fb4d2ab6ed9be9b2810571f2189149d9dfd08f1f8abe05475d3f445230413f3c2d8d7a32ff4c27a59f3922a9523e81ceb9a2b0a35482808b08de05f2686e7dd5fd0b5cf19516cb0558a76d4d97770e28404a2e78dcb63f3b96eb19f1bbf291e88a6145eb27e6fa5ed3609e829094f9cf937c58dd2bbeac045e27afa19407b425abe489f434d671692197d5cf72c07c8e2ac447b2db81b8e0e248d621f0521aeedf472184eff3527a64c13f0c601b3ec2048af2672255c14010fb1430fdd8b36f426c6469ce3c6b0be2cac2ae4ceea8baadd17bd0900a75114a14a370e499163de5a0e817ec703ec5b636140892fa8203d20c032cbd574fc34d2385320f8ef889048179132035bf18bc196f5b08bc5e4d0ae4bf18768342d5b48f90d27c5eda7c200bb025a2de774ef9a8dfd0d75fd844969950a13f5b4320a250d4a124fd0beae2313ac372d0583b79543c07fe4a31afc144acf99238c31b3e0b9c6241225467d7ef6a633313309fa84669b737cda0632d2dd36ec901e1ae3e4a68210fa0b0fa02588eff6e72f73f015951e42f85868c622a8be41f3247a2140f3fc0a30d31f2379fd387240e5f306a17b035720f57dba876e1cd0d33bc87d966d620e65736370fc817ac4ff15db12147cc85c230ebef736047408e03ca8f0be6a642030b5627458aa1e65dd56737ab810b2a8b492586c42e193ce28d9aae97a13db0cc36d358f6efdfc379ee394140a6700fd99e86b8f7d3a594cceb4bbefff68ce15866cdfc9894097f08e89b66f8c30bffcc001044b0594922e11b8a055c9f6ee026ae31ac020370a1f3e06daa5a772272941a101c11714fe8d8e37d40c3122c61d8f610d194cf82da8419cfa1680eb14ee96b6686e9f209a00df36e96a9d6f842571b23c753c5577e9ed466bb585e8910722050a93a8463bb655ccac5d9900a31a460738ff88a624304a1e5b37fb9ffb23887000c006db10e2b4b8985770d88102ff55d36e7513f37acb4c5920fbd220fc20c8b9b1a83128b0c64bcf26f761bc01407ff23a9b11942b6034278ae1b4817169180989eef86eba3f781c6ed7c82b279089823170533c0791d57ca17f1beb9bd6fda0242993bf5be154fa2377340d0c0c8fcd0da22a25000793deaaf634421431ac4d940c6ccbe9265f52eafaa6660cffe9ce6f0ae13c38cb7ce5304e697ff3814a992c030ff909d9b33615c8f9be2f199990be4b96c115324f1f53c609d9c70c9a3d643973be4f407aa54cfcb7741124c48570a6a3906b2eaa017d1c4e98b1336932976e7ac4b2cc3ff548a3d5272ca9b66092c0be62c903cc5f6022fe892c37fbaaa5bba75f41e40b410b223b052ed39b580fcc936b9582cf7c479ce148f56a374df66ddb07dd6547ed67da09e72e7820676bd33cc3dcc778b5e5929677908c6f43d3f2e793b768cb2bb6c11cac258438c7630aa10ec88ac11e66c0855e0be63beebbc4a846a4210d3b16a1a45b07144aa8b8587d6d981683640bcdb7390599cc6b806869d01e425cc59f78d26e009412daa789b0872610de5d576f928321c80f83d64dd5cac4562cfbd72b56bd17165c7c60feb78c973289e4afbb58225262f916b2e189f9195aaaaef30f037d1a0b47a5e1ad425f16c581f4317b4a1cce0458eb64e01a2ce88067b6365f20570e7a6ef374c26e7df9d9b86f1a2ed3f3a6f151d108f24245346f7fbacc41e39d1b89bbc83ce93442bd733e4fb70c090135e71518d07f6f7cb5484256d973b5e107cadc46b3caecb4a0c85930d59631ae69c164b9b51d4af12b414da442ca6e2e2e905b3d35c848bc74803d93cdd1a52642f101ecb76adfbb324aab602cda06f4142f88ea187aa2d9bc2770fb55738bf1c22a0172dd3aaaf0756da7919ea8fd480bd1852cd97487ce18b5406f1e208103de94a56189a902b0e677d3ebb09b9b4403e78dbd0aa42f5e177f3c0e44bff9304af9cfc15d61d2b7172aaff7db8e31c401014a1d5637a540050a7c24f8ce92509be63c99f33c3e169be4e48c5c46c0d00536d73fdfaacd543fc05f62e194e64df3916521560c2e7c70651b0c8cd1711e13cf10ed6d7db7642d832dac4b6699b200a8bae01e5c271a7cb1dd55be7593252ec16d138fea76b7ab76eb2597f4847ffa61f1e5560f85d5b1dfb976384bd384137d0d599a996035b696364b2bbd910add9dd520e1d50d4acbaa28865a9eceb70caf6cfd4e46b787fcef171be5316b41061ad0f844d78c571948c0c3b9ca5b581e66da9006818668cb3024873fb6c209e388964e71e4e7481f60e98293614a491c4343db492b43eaf5acc1b24720b645f73a741b8082ec8e7f0940270bd337222e0e5f38ae454a0bbbb97e2684eb4c0f8665e2f5fb567a7fc8f46d42d76a87b9167d5caaa34cac8c6c35e92f339daa280b074923950ca4fbe26d12fc08df611b122b294ee8165d619809c84a8483ac031a91a321ba578cfff08ff1da3638da881a658ee5025ca8cd4c2412bd64fec5acce4a063c903a928a1bbe47a19d6f20ce2a45e48b775e956e7328e2807bdf0699717624dcc4bea3f49696076d6bd1f3b32648c84b627afc98ee7a255a9b6a7688bc2cb1d022b456c914d73c31a61cdf1f0990d46e50809e625fee7f66f13f3fd3d711053420474e2137299fb499c55d280fadb264bfa1c01fb103c85e1025144416fb45c410b1de94ce3b6b39e31dbdd40085c18596d5a4f3aa5d72f5d762f32657466c3d6cfa119e4b15d66c166f9235130b6b5db7bdeb688674c2c9ffe5953f913dc8b70c89ee3a3e9d0b900d3eb11b0c998e9028b8af68a3a123f0a5aa56f987cb287bed1b4ec7ea4d5080f7460191140fbbc521c4b4ca284d2f52ba94d12c57cffdf6b3a1823c8bec3f227f0964d0269567969b2a6952d67b2cf3fe4ed64ad32021de8df302d6fa0e5934dcb0fe0207c4eaf81924185e0ae50fa740e900c098f17483a8c8e9acbd142b0d3b8d7ed31222b7d9d9642133a4d710850b29be1b312f2043e0553468a03a468da16119a0147bc6655daaeeaccef77df33300f0304b229d0a67f2e2686ed0faf697a8c0be02a92c5eecec61f86730c46d3e4060cedea01e6ad69edab787224daf217fc00d25bc51eaadc89bea9ed8b8b064d00a603bba923afd50feeaa28480fe0bf5208e1f6c3c1ab06c7581870abe1d4263967bca1c83e6d829d9967b15b03c71662b051fef32423b6f60529ef22b34e79fd39a03188920fb378c832b94ae04ee9c1e611c19e994f3fa9a123454d8453ff1ab96e3dfaf32adcce9ce3a58d3ab46800283017c41df169d9030faa8635070293a6b4f92c2a03a23f322f9219eea5c13a65e16f8cfbb4c12898a9b5666c005ee1bfaa0d64396e1ffb0c89394ac75ce404e341592c70ec8ef696ac200a491dc290b060b0d8ab0b0d73a785117cb992fd5082920edf8a802617e40021efd904982120cb763677cfbf5fb26e040250ff71a9e8f0a74f8c8c0cbb2d83b70592dcad430759e5e3365da322b45198c34067f1ef09108461a7aff586e522eff5dd06ba39f32adbbb2ac11cb5ed218b080ef6b47ab0a21d7658d2c57a0b782547f8c5acce7c340cbc4e32291a4f4d261d51eb5d19fc313f13da04d155cee22bdda2a05328dfad96d058d2752e41372178a8ac3069d27277f5ad615312c8a0da2d6f5967a6191fdfa66a465ba370e3d8a8b27f31b75cc1d1dfc9e914df09e8044c0d753950f5a662bdec0dc6b50c3fac197df947b2fe927b02824b638eba85cc6b3a3a4633ea595e9e7c01764122a2a188c7f05b86a0901781fe9cf000e194128b474d8334de7204e7cf9ba4c45a82851cc873cb2d851257d3c067a363b22d3ffc4592989e1cdd50333e7d20858eaded8fa02b85209c329cb4ba3f99922719ac16e761fee300db4c4022070de18149af7d30df3be006208f5703c677000acfdbdab00aa099e0c515cdd6c36e4c2fcb14d4a169ac5226206d780418161648be96d949099a7cd18b491d72cb00baf6a36552c49578cb858004493fa5ffabec5316a34f8af4e3e329c28ace755c8329add179dd15a716fc30cae70fd816fb9ae67f0e595a39585efe683dac6569cecd5b4612600ca7e58142774ac04ad50ebf3fd3ffbe32cfdacc60c4942daa5b1dbd52cb4570ce3acffe92365dd10cb6526daa7ba1a0b8635167f7220c97ce29b11fff0e2780c3d43d82700b6511664384bafbc8ace53cb17529a7cbe4fa1e5b8242a149af5120092d5f50af044e091387602eb00690a21ed41b2b6cd2ea58ffd23c273f07932cea2d0e81664d0b1770c1a8726cbf09e109bb62cf0bbd05bd068eeb95e2f120470b4082e0f037bed194d5aaedd71fdb36b71ebabfc29ba3fc10086cf780c1cdc74ebe0250fa517cbde50830063cefcc39e7848ae2f4ca899903296fefa908e8c18e1bb8a1437ac612ce96cf6c0617a6cd6c6e8a631315a1d3e29d803b558c3a4274960d005b212c9c31e65649812c0dc066ae224f5001a4893fdad31cf49271b8df0353c2feb0c6796198d5ddff04951416833ce4200813f0f39d2ce75da2985d4329c1f2b2023d34a62804670f522d449b1edbf12c255673077c821683b1d8d7e5ddf800a4b7b539cba5dcaf1fdb336daec57ba5cc8e484c04b5e70f74ee3a0a2e954e2175117e97ac2c5bdc2dd375946a0355561c2cb8edb457d5185a93189e13a4aea066c51edf64ec0704d127db99b2ce23ca840435bc4ce9d8b533e23bd09c80f5004b23d8666c77a078368ad040c4e1ce3d4fddf9ed1b1863c9ed1d2eea60ed85f24edbcea0b513edb75f6321e428db3b3082154a7923b2776b96f9276f251b155188c9415d7e4f09e37f89fbaddef8410979efa36c6a1efe5565349a96498aea518ac498adad8a301e7d171c35883c89798ca890032253d77b00393525dfc1c992b73dcd9fdf3f304f8b5c1f7a71f4036d4095ffca43ba1c2ed25761063f0ee3925fcb36659ca28e692b6f5e89599405f990c57ec7237311d663bdafde7d2f81d0dcc1c522e2497eb105f9d9bb4f969cb1c89c2c4010dead0a19023f4bf15879c2f13afbd1caf80f6e3b5628c1e36f57189ec2f5a61d03eaa58e379df3027da6f24403ef8d186c62c5176573cdb2e799c65bb6c0da4fcaba21d4b8c02afeb6b8717fe4b74f45e45025aaacd2ee55901d81ab63d04853f222695e0969e3a9f89b9297aa44364a7a2bcd0be5759f3a27bc00d7a99f32b69cce48cbba3cc858a9fc708deb0b9515755e48edb793a598de521bfecb827ca3fe1e58b9875426b76a268137ba4e5fe24a5d7f977fe9d1b50ce573fe98be883ce057ff046e26d999403f52ff08e8a7dfb7f993a389212ea2ed1bf7725f4a265bdbfc208fcda3df33465c90928f1e1bb77db1955ef73583031893947c902eecff3fe24903319d5c2433e0b0546305540bd96a71fda765fcad36af678a4f07cc95fcd02e81ac2760931d40c132c998a8c0b120abd2d3e666518d1ef5ffa130cca13b78e57cad7a151bb594a1e394e73d71aea3c2d6e5cbbff872c98714fb5fb571e1e3743eea704b295666d21ce4e84ca11a1d673a27b169c5f83088e0ed344fd75dd4b741b05226ab93513305f4a7f83ded532c62d697e04ba2a963e30c321f916c0080e9fbc5ab619aa8c220f38de69ea6d5a49d99f904ace4f9c6132a8708e225fa2ab197c4508ff25c42a92d2a7cc3855cd59f6c96407266922cfb6f523d46e47e29d0447f32582e1951a6b0361e26226f8379a50f2d5e3204c6828da40b8e7cdc816f6b166ef4aa723077f7be16b732f1edab20a5668e50844b717dacc74dd94451cb9460cca88ad8a22ff47083c488ffc04e3b9c39ec7b25f41ee2a2a6cae755bc5d1fb9e1633549013968d569bb6c6f0d95547d93eba03a1da40766f3ab3b18f19b30964af1d69db15b13c2024eb855dd70af579f46fad36a77a77c2cf65812e92125f2f9e4011eb2ccf4716ddca655668da9838e29677662101824afbe875bd3490fa1a7a286ae31704b40587eec531af8bf89a26692686387927daec1d6e004fe9105160219a7d23035a362fa532f57b6269292058de35d8cd36c4c6b1143821adbe16379d11e1232646f4d497651f0954ea9a1df894dfc491003980f60b52cc0a1ab26756077227ee3102ea57dce3271965fbc8bd6e66a68a2b9543b577278024cd4536b2741226c3b1d409828f4e0d9874ecd7069da3c9c77f290915adcdf6b456983796b3fc28042c760483b93e07a9ac85a3991713d1c2f85b94556c6d1a273da1ee90265d01a8981245c7f7f8d10b2915126d9f9ac61a2d6a8c055ea87be9bafcb5643c9b119120d465b5ddb005f7ccba998fd30058ffa33d82c9d8e4e301e780bd7f93832604cfea9de23f1d6b2a50f65047ac9f879eff0451f311164941d680b76395aa208016ec3c5dfd1e4e2451ec13fb58f2242b1c26e54bbd88b44e163e4e7c7aad0b3d5897b5f0d8d49522bd7b5c1aeabc1faa5f92051b7c9609bf3dabb085a533130bb3f740556f8e71453758b79613a124d615ab01b2e1de8a4e519947fcd62b059aca90452fd3b086e6f37ec6b9c8c565885d7bc2125f2a6edc128992c986ed1e16fd93e88a03407f497ac742f49836ac0886ff6354b8a0d7096dd464cbbd9e1182fbc13662c46028cf859e93de493b32a8060828596b2fc4245655a4f9589c0c3e6a3130d7703084bca793e15a9e9d6366f1898695e5d47b7da2fe660461450c47a7186e240f9f600739c2fa2038f28aa34075563b42e39189fff917f77b1d169abf6085508100e18e9651f68113f0371c993ba004d5ac582763655c4df79c28668406aa60c8966a70355446b5ef97eca8f9c21cd311efbd65d6701932662d214ba804cb02a5d0c4e936c50435a6d8a2678558c26aedd38694ee726b9fd89a039a0378b17166714bb1379bb173d2e916411278d8285e9e6dc901a616904b1927280ff562b631da5d3cc81ff70e3269a358e8c4dca3696ccdb546ae1147014621b472511388d61cb1e60590ad87f05617f122417a4282d94bbc643f0547a6682008695507d53505be0bd9b78ed3c7adb01360df7cc3b4e8ac5357485facb49c2e0600e9a923c71d12fcaffcddc8de640403018a9a2ff1c2ebcdfd6700facbbb2dbc42db37fa4e0b64fa45559ed421cf9e91bbb8533983393113a75023ebc8090bb50f8d0b75903c3c8e4b51137f3ccf2b5ee42e549fa6c2c57a336718e1867420e656c75e985702a0b1d78e451a4d0e7abfe681ecfd92e1fe5d00c00a22acdd0ff415858ee7fa9d195fbd1071d672106427bcd2c8f5114e6b4e3b8f6fba28c62b38318cbd8088a2b956ddcdebbcfe0e6670d37b63276fa30a16791a3fee1b9d", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_insecure_agg/report.md b/circuits/benchmarks/results_insecure_agg/report.md index c8c767db5..5913ec05d 100644 --- a/circuits/benchmarks/results_insecure_agg/report.md +++ b/circuits/benchmarks/results_insecure_agg/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-22 17:39:41 UTC +**Generated:** 2026-05-23 11:19:46 UTC **Git Branch:** `feat/1549` -**Git Commit:** `f5c2fef8490fc34fe7357743220321af9626c879` +**Git Commit:** `8a841717468169df094b7316e57e1008d4ba34b0` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -27,7 +27,7 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the | Rayon worker threads | 13 | | CPU cores (host) | 14 | | `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | -| Verbose logging (`run_benchmarks.sh --verbose`) | false | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | ### Hardware & software (Nargo / Barretenberg host) @@ -72,39 +72,39 @@ Single-circuit `bb prove` on the benchmark oracle witness (not the integration a | Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | | -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 6847 | 0.13 | 27.07 | 15.88 | -| C1 | 57818 | 0.33 | 25.03 | 15.88 | -| C2a | 142625 | 0.83 | 25.68 | 15.88 | -| C2b | 198355 | 0.90 | 26.57 | 15.88 | -| C3a | 132633 | 0.78 | 25.49 | 15.88 | -| C3b | 132633 | 0.78 | 25.49 | 15.88 | -| C4a | 92515 | 0.50 | 25.70 | 15.88 | -| C4b | 92515 | 0.50 | 25.70 | 15.88 | -| C5 | 151717 | 0.80 | 25.71 | 15.88 | -| user_data_encryption | 53732 | 0.34 | 25.17 | 15.88 | -| C6 | 86927 | 0.52 | 24.84 | 15.88 | -| C7 | 104273 | 0.51 | 25.71 | 15.88 | +| C0 | 6847 | 0.12 | 26.22 | 15.88 | +| C1 | 57818 | 0.34 | 25.94 | 15.88 | +| C2a | 41244 | 0.31 | 25.78 | 15.88 | +| C2b | 79591 | 0.49 | 26.23 | 15.88 | +| C3a | 120114 | 0.57 | 26.46 | 15.88 | +| C3b | 120114 | 0.57 | 26.46 | 15.88 | +| C4a | 67494 | 0.46 | 26.53 | 15.88 | +| C4b | 67494 | 0.46 | 26.53 | 15.88 | +| C5 | 123624 | 0.55 | 26.63 | 15.88 | +| user_data_encryption | 53732 | 0.33 | 25.67 | 15.88 | +| C6 | 86927 | 0.53 | 27.16 | 15.88 | +| C7 | 90841 | 0.46 | 26.43 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042548 | 176196 | 3218744 | -| Π_user | 15.88 KB | 0.12 KB | 2973097 | 170416 | 3143513 | -| Π_dec | 10.69 KB | 3.47 KB | 3553726 | 187308 | 3741034 | +| Π_DKG | 10.69 KB | 0.47 KB | 3125294 | 176148 | 3301442 | +| Π_user | 15.88 KB | 0.12 KB | 2972881 | 170272 | 3143153 | +| Π_dec | 10.69 KB | 3.47 KB | 3640985 | 187260 | 3828245 | ### Role / Phase / Activity | Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | | --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 159.57 s | 127.00 KB | 128.19 KB | -| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 131.58 s | 10.69 KB | 11.16 KB | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 144.27 s | 127.00 KB | 128.19 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 131.81 s | 10.69 KB | 11.16 KB | | User | P3 | per user input | isolated_nargo | 0.65 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.52 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 68.10 s | 10.69 KB | 14.16 KB | -| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 57.87 s | 10.69 KB | 14.16 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.53 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 53.36 s | 10.69 KB | 14.16 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 49.52 s | 10.69 KB | 14.16 KB | -_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **24.69 s** — not +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **20.40 s** — not comparable to P2 wall_clock row above._ ## Integration test (`test_trbfv_actor`) @@ -114,72 +114,72 @@ comparable to P2 wall_clock row above._ | Phase | Metric | Duration (s) | | ------------------------------------------------------------------ | ------------ | ------------ | | Starting trbfv actor test | `wall_clock` | 0.00 | -| Setup completed | `wall_clock` | 3.04 | -| Committee Setup Completed | `wall_clock` | 20.25 | -| Committee Finalization Complete | `wall_clock` | 0.01 | -| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 131.58 | -| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 159.57 | -| E3Request -> PublicKeyAggregated | `wall_clock` | 162.13 | -| Application CT Gen | `wall_clock` | 0.31 | +| Setup completed | `wall_clock` | 2.66 | +| Committee Setup Completed | `wall_clock` | 20.17 | +| Committee Finalization Complete | `wall_clock` | 0.00 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 131.81 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 144.27 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 144.78 | +| Application CT Gen | `wall_clock` | 0.01 | | Running FHE Application | `wall_clock` | 0.00 | -| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 57.87 | -| Ciphertext published -> PlaintextAggregated | `wall_clock` | 68.10 | -| Entire Test | `wall_clock` | 253.85 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 49.52 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 53.36 | +| Entire Test | `wall_clock` | 220.99 | ### Multithread job timings (`tracked_job_wall`) | Name | Avg (s) | Runs | Total (s) | | ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.12 | 3 | 0.37 | -| CalculateDecryptionShare | 0.63 | 3 | 1.88 | -| CalculateThresholdDecryption | 0.56 | 1 | 0.56 | -| GenEsiSss | 0.17 | 3 | 0.50 | -| GenPkShareAndSkSss | 0.24 | 3 | 0.72 | -| NodeDkgFold/c2ab_fold | 7.62 | 3 | 22.87 | -| NodeDkgFold/c3a_fold | 35.48 | 3 | 106.43 | -| NodeDkgFold/c3ab_fold | 7.55 | 3 | 22.65 | -| NodeDkgFold/c3b_fold | 35.65 | 3 | 106.94 | -| NodeDkgFold/c4ab_fold | 7.20 | 3 | 21.59 | -| NodeDkgFold/node_fold | 17.67 | 3 | 53.02 | -| ZkDecryptedSharesAggregation | 8.41 | 1 | 8.41 | -| ZkDecryptionAggregation | 49.42 | 1 | 49.42 | -| ZkDkgAggregation | 20.37 | 1 | 20.37 | -| ZkDkgShareDecryption | 3.17 | 6 | 19.02 | -| ZkNodeDkgFold | 111.17 | 3 | 333.52 | -| ZkPkAggregation | 4.33 | 1 | 4.33 | -| ZkPkBfv | 0.43 | 3 | 1.30 | -| ZkPkGeneration | 5.17 | 3 | 15.52 | -| ZkShareComputation | 7.41 | 6 | 44.47 | -| ZkShareEncryption | 7.47 | 24 | 179.24 | -| ZkThresholdShareDecryption | 7.68 | 3 | 23.03 | -| ZkVerifyShareDecryptionProofs | 0.11 | 3 | 0.34 | -| ZkVerifyShareProofs | 0.24 | 5 | 1.21 | - -Sum of tracked job wall time: **1037.71 s** — **not** end-to-end latency (jobs run in parallel up to +| CalculateDecryptionKey | 0.01 | 3 | 0.02 | +| CalculateDecryptionShare | 0.02 | 3 | 0.06 | +| CalculateThresholdDecryption | 0.02 | 1 | 0.02 | +| GenEsiSss | 0.01 | 3 | 0.02 | +| GenPkShareAndSkSss | 0.01 | 3 | 0.03 | +| NodeDkgFold/c2ab_fold | 8.17 | 3 | 24.52 | +| NodeDkgFold/c3a_fold | 35.19 | 3 | 105.58 | +| NodeDkgFold/c3ab_fold | 7.49 | 3 | 22.47 | +| NodeDkgFold/c3b_fold | 34.99 | 3 | 104.97 | +| NodeDkgFold/c4ab_fold | 7.89 | 3 | 23.68 | +| NodeDkgFold/node_fold | 18.33 | 3 | 55.00 | +| ZkDecryptedSharesAggregation | 1.56 | 1 | 1.56 | +| ZkDecryptionAggregation | 47.95 | 1 | 47.95 | +| ZkDkgAggregation | 19.77 | 1 | 19.77 | +| ZkDkgShareDecryption | 1.23 | 6 | 7.38 | +| ZkNodeDkgFold | 112.07 | 3 | 336.22 | +| ZkPkAggregation | 0.63 | 1 | 0.63 | +| ZkPkBfv | 0.22 | 3 | 0.66 | +| ZkPkGeneration | 2.36 | 3 | 7.09 | +| ZkShareComputation | 2.39 | 6 | 14.34 | +| ZkShareEncryption | 4.00 | 24 | 95.92 | +| ZkThresholdShareDecryption | 3.39 | 3 | 10.18 | +| ZkVerifyShareDecryptionProofs | 0.10 | 3 | 0.31 | +| ZkVerifyShareProofs | 0.27 | 5 | 1.37 | + +Sum of tracked job wall time: **879.74 s** — **not** end-to-end latency (jobs run in parallel up to `BENCHMARK_MULTITHREAD_JOBS`). ### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) | Step | Avg (s) | Runs | Total (s) | | --------- | ------- | ---- | --------- | -| c2ab_fold | 7.62 | 3 | 22.87 | -| c3a_fold | 35.48 | 3 | 106.43 | -| c3ab_fold | 7.55 | 3 | 22.65 | -| c3b_fold | 35.65 | 3 | 106.94 | -| c4ab_fold | 7.20 | 3 | 21.59 | -| node_fold | 17.67 | 3 | 53.02 | +| c2ab_fold | 8.17 | 3 | 24.52 | +| c3a_fold | 35.19 | 3 | 105.58 | +| c3ab_fold | 7.49 | 3 | 22.47 | +| c3b_fold | 34.99 | 3 | 104.97 | +| c4ab_fold | 7.89 | 3 | 23.68 | +| node_fold | 18.33 | 3 | 55.00 | ### Aggregation jobs (`tracked_job_wall`) | Operation | Avg (s) | Runs | Total (s) | | ---------------------------- | ------- | ---- | --------- | -| ZkDecryptedSharesAggregation | 8.41 | 1 | 8.41 | -| ZkDecryptionAggregation | 49.42 | 1 | 49.42 | -| ZkDkgAggregation | 20.37 | 1 | 20.37 | -| ZkNodeDkgFold | 111.17 | 3 | 333.52 | -| ZkPkAggregation | 4.33 | 1 | 4.33 | +| ZkDecryptedSharesAggregation | 1.56 | 1 | 1.56 | +| ZkDecryptionAggregation | 47.95 | 1 | 47.95 | +| ZkDkgAggregation | 19.77 | 1 | 19.77 | +| ZkNodeDkgFold | 112.07 | 3 | 336.22 | +| ZkPkAggregation | 0.63 | 1 | 0.63 | -Sum of aggregation job tracked time: **416.04 s** (parallel CPU work; not P1/P2 wall clock). +Sum of aggregation job tracked time: **406.13 s** (parallel CPU work; not P1/P2 wall clock). ### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) diff --git a/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json b/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json index ffa8b6ee6..ac9e71558 100644 --- a/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json +++ b/circuits/benchmarks/results_insecure_no_agg/benchmark_run_meta.json @@ -3,7 +3,7 @@ "bfv_preset_subdir": "insecure-512", "proof_aggregation": false, "multithread_jobs": 13, - "verbose": false, + "verbose": true, "nodes_spawned": 20, "committee_size_n": 3, "network_model": "in_process_bus", diff --git a/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json b/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json index 1a64e490c..af8f2834e 100644 --- a/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json +++ b/circuits/benchmarks/results_insecure_no_agg/crisp_verify_gas.json @@ -1,7 +1,7 @@ { "verify_gas": { "dkg": null, - "user": 2972965, + "user": null, "dec": null }, "source": "folded_proof_export_plus_crisp_verify_test", @@ -46,42 +46,42 @@ "proof_aggregation_enabled": false, "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.129886722, "runs": 3, "total_seconds": 0.389660166 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 0.623894805, "runs": 3, "total_seconds": 1.871684416 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 0.554217625, "runs": 1, "total_seconds": 0.554217625 }, - { "name": "GenEsiSss", "avg_seconds": 0.150156208, "runs": 3, "total_seconds": 0.450468624 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 0.240268944, "runs": 3, "total_seconds": 0.720806833 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.443416042, "runs": 1, "total_seconds": 8.443416042 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 2.964831979, "runs": 6, "total_seconds": 17.788991876 }, - { "name": "ZkPkAggregation", "avg_seconds": 2.12222325, "runs": 1, "total_seconds": 2.12222325 }, - { "name": "ZkPkBfv", "avg_seconds": 0.433216972, "runs": 3, "total_seconds": 1.299650917 }, - { "name": "ZkPkGeneration", "avg_seconds": 4.951818527, "runs": 3, "total_seconds": 14.855455582 }, - { "name": "ZkShareComputation", "avg_seconds": 7.233157423, "runs": 6, "total_seconds": 43.398944541 }, - { "name": "ZkShareEncryption", "avg_seconds": 7.581401666, "runs": 24, "total_seconds": 181.953639998 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 7.736885083, "runs": 3, "total_seconds": 23.210655251 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.10097575, "runs": 3, "total_seconds": 0.30292725 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.227720524, "runs": 5, "total_seconds": 1.138602624 } + { "name": "CalculateDecryptionKey", "avg_seconds": 0.119593152, "runs": 3, "total_seconds": 0.358779458 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.625714819, "runs": 3, "total_seconds": 1.877144457 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.555039542, "runs": 1, "total_seconds": 0.555039542 }, + { "name": "GenEsiSss", "avg_seconds": 0.126707139, "runs": 3, "total_seconds": 0.380121417 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.256695819, "runs": 3, "total_seconds": 0.770087459 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 8.225412791, "runs": 1, "total_seconds": 8.225412791 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 2.350571951, "runs": 6, "total_seconds": 14.103431707 }, + { "name": "ZkPkAggregation", "avg_seconds": 1.971270875, "runs": 1, "total_seconds": 1.971270875 }, + { "name": "ZkPkBfv", "avg_seconds": 0.429601166, "runs": 3, "total_seconds": 1.2888035 }, + { "name": "ZkPkGeneration", "avg_seconds": 2.953823722, "runs": 3, "total_seconds": 8.861471166 }, + { "name": "ZkShareComputation", "avg_seconds": 3.007209458, "runs": 6, "total_seconds": 18.04325675 }, + { "name": "ZkShareEncryption", "avg_seconds": 5.763399979, "runs": 24, "total_seconds": 138.321599497 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 7.663020986, "runs": 3, "total_seconds": 22.989062958 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.102607666, "runs": 3, "total_seconds": 0.307823 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.257090858, "runs": 5, "total_seconds": 1.285454293 } ], - "operation_timings_total_seconds": 298.501344995, + "operation_timings_total_seconds": 219.33875887, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, - { "label": "Setup completed", "seconds": 3.065066, "metric": "wall_clock" }, - { "label": "Committee Setup Completed", "seconds": 20.247525958, "metric": "wall_clock" }, - { "label": "Committee Finalization Complete", "seconds": 0.005479542, "metric": "wall_clock" }, - { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 2.132179, "metric": "wall_clock" }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 29.416552334, "metric": "wall_clock" }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 31.983316666, "metric": "wall_clock" }, - { "label": "Application CT Gen", "seconds": 0.307567583, "metric": "wall_clock" }, - { "label": "Running FHE Application", "seconds": 0.003297708, "metric": "wall_clock" }, - { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 8.471012, "metric": "wall_clock" }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 18.676276166, "metric": "wall_clock" }, - { "label": "Entire Test", "seconds": 74.289529916, "metric": "wall_clock" } + { "label": "Setup completed", "seconds": 3.058201042, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.257082542, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.005129541, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 1.981342, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 22.251965458, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 24.866107292, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.309383, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.003993125, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 8.253253, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 18.401028833, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 66.904175584, "metric": "wall_clock" } ], "folded_artifacts": null }, "test_exit_code": { - "crisp": 0, + "crisp": 1, "folded_export": 0, "enclave_contracts": 0 } diff --git a/circuits/benchmarks/results_insecure_no_agg/integration_summary.json b/circuits/benchmarks/results_insecure_no_agg/integration_summary.json index 898ab8a0b..73e312b4f 100644 --- a/circuits/benchmarks/results_insecure_no_agg/integration_summary.json +++ b/circuits/benchmarks/results_insecure_no_agg/integration_summary.json @@ -23,96 +23,96 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.129886722, + "avg_seconds": 0.119593152, "runs": 3, - "total_seconds": 0.389660166 + "total_seconds": 0.358779458 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 0.623894805, + "avg_seconds": 0.625714819, "runs": 3, - "total_seconds": 1.871684416 + "total_seconds": 1.877144457 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 0.554217625, + "avg_seconds": 0.555039542, "runs": 1, - "total_seconds": 0.554217625 + "total_seconds": 0.555039542 }, { "name": "GenEsiSss", - "avg_seconds": 0.150156208, + "avg_seconds": 0.126707139, "runs": 3, - "total_seconds": 0.450468624 + "total_seconds": 0.380121417 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 0.240268944, + "avg_seconds": 0.256695819, "runs": 3, - "total_seconds": 0.720806833 + "total_seconds": 0.770087459 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 8.443416042, + "avg_seconds": 8.225412791, "runs": 1, - "total_seconds": 8.443416042 + "total_seconds": 8.225412791 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 2.964831979, + "avg_seconds": 2.350571951, "runs": 6, - "total_seconds": 17.788991876 + "total_seconds": 14.103431707 }, { "name": "ZkPkAggregation", - "avg_seconds": 2.12222325, + "avg_seconds": 1.971270875, "runs": 1, - "total_seconds": 2.12222325 + "total_seconds": 1.971270875 }, { "name": "ZkPkBfv", - "avg_seconds": 0.433216972, + "avg_seconds": 0.429601166, "runs": 3, - "total_seconds": 1.299650917 + "total_seconds": 1.2888035 }, { "name": "ZkPkGeneration", - "avg_seconds": 4.951818527, + "avg_seconds": 2.953823722, "runs": 3, - "total_seconds": 14.855455582 + "total_seconds": 8.861471166 }, { "name": "ZkShareComputation", - "avg_seconds": 7.233157423, + "avg_seconds": 3.007209458, "runs": 6, - "total_seconds": 43.398944541 + "total_seconds": 18.04325675 }, { "name": "ZkShareEncryption", - "avg_seconds": 7.581401666, + "avg_seconds": 5.763399979, "runs": 24, - "total_seconds": 181.953639998 + "total_seconds": 138.321599497 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 7.736885083, + "avg_seconds": 7.663020986, "runs": 3, - "total_seconds": 23.210655251 + "total_seconds": 22.989062958 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.10097575, + "avg_seconds": 0.102607666, "runs": 3, - "total_seconds": 0.30292725 + "total_seconds": 0.307823 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.227720524, + "avg_seconds": 0.257090858, "runs": 5, - "total_seconds": 1.138602624 + "total_seconds": 1.285454293 } ], - "operation_timings_total_seconds": 298.501344995, + "operation_timings_total_seconds": 219.33875887, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { @@ -122,57 +122,57 @@ }, { "label": "Setup completed", - "seconds": 3.065066, + "seconds": 3.058201042, "metric": "wall_clock" }, { "label": "Committee Setup Completed", - "seconds": 20.247525958, + "seconds": 20.257082542, "metric": "wall_clock" }, { "label": "Committee Finalization Complete", - "seconds": 0.005479542, + "seconds": 0.005129541, "metric": "wall_clock" }, { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 2.132179, + "seconds": 1.981342, "metric": "wall_clock" }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 29.416552334, + "seconds": 22.251965458, "metric": "wall_clock" }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 31.983316666, + "seconds": 24.866107292, "metric": "wall_clock" }, { "label": "Application CT Gen", - "seconds": 0.307567583, + "seconds": 0.309383, "metric": "wall_clock" }, { "label": "Running FHE Application", - "seconds": 0.003297708, + "seconds": 0.003993125, "metric": "wall_clock" }, { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 8.471012, + "seconds": 8.253253, "metric": "wall_clock" }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 18.676276166, + "seconds": 18.401028833, "metric": "wall_clock" }, { "label": "Entire Test", - "seconds": 74.289529916, + "seconds": 66.904175584, "metric": "wall_clock" } ], diff --git a/circuits/benchmarks/results_insecure_no_agg/report.md b/circuits/benchmarks/results_insecure_no_agg/report.md index 94b51a9bd..37d834f75 100644 --- a/circuits/benchmarks/results_insecure_no_agg/report.md +++ b/circuits/benchmarks/results_insecure_no_agg/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-22 17:39:43 UTC +**Generated:** 2026-05-23 10:09:34 UTC **Git Branch:** `feat/1549` -**Git Commit:** `f5c2fef8490fc34fe7357743220321af9626c879` +**Git Commit:** `8a841717468169df094b7316e57e1008d4ba34b0` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -27,7 +27,7 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the | Rayon worker threads | 13 | | CPU cores (host) | 14 | | `dkg_fold_attestation_verifier` | _(disabled — proof aggregation off)_ | -| Verbose logging (`run_benchmarks.sh --verbose`) | false | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | ### Hardware & software (Nargo / Barretenberg host) @@ -45,7 +45,7 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the ## Audit status -> **Incomplete on-chain verify gas:** 2 of 3 artifact verify-gas values are **N/A**. Re-run +> **Incomplete on-chain verify gas:** 3 of 3 artifact verify-gas values are **N/A**. Re-run > `./run_benchmarks.sh` and ensure `extract_crisp_verify_gas.sh` completes (CRISP test + > `test_trbfv_actor` + EVM replay). Calldata gas alone is not sufficient for audit sign-off. @@ -74,26 +74,26 @@ Single-circuit `bb prove` on the benchmark oracle witness (not the integration a | Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | | -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 6847 | 0.12 | 25.14 | 15.88 | -| C1 | 57818 | 0.36 | 25.40 | 15.88 | -| C2a | 142625 | 0.83 | 26.78 | 15.88 | -| C2b | 198355 | 0.85 | 24.42 | 15.88 | -| C3a | 132633 | 0.81 | 31.38 | 15.88 | -| C3b | 132633 | 0.81 | 31.38 | 15.88 | -| C4a | 92515 | 0.48 | 24.11 | 15.88 | -| C4b | 92515 | 0.48 | 24.11 | 15.88 | -| C5 | 151717 | 0.79 | 27.23 | 15.88 | -| user_data_encryption | 53732 | 0.32 | 25.77 | 15.88 | -| C6 | 86927 | 0.53 | 24.62 | 15.88 | -| C7 | 104273 | 0.49 | 25.28 | 15.88 | +| C0 | 6847 | 0.13 | 26.12 | 15.88 | +| C1 | 57818 | 0.34 | 27.04 | 15.88 | +| C2a | 41244 | 0.32 | 27.34 | 15.88 | +| C2b | 79591 | 0.52 | 27.21 | 15.88 | +| C3a | 120114 | 0.58 | 26.59 | 15.88 | +| C3b | 120114 | 0.58 | 26.59 | 15.88 | +| C4a | 67494 | 0.46 | 26.91 | 15.88 | +| C4b | 67494 | 0.46 | 26.91 | 15.88 | +| C5 | 123624 | 0.57 | 25.66 | 15.88 | +| user_data_encryption | 53732 | 0.34 | 27.20 | 15.88 | +| C6 | 86927 | 0.53 | 26.33 | 15.88 | +| C7 | 90841 | 0.49 | 26.71 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 15.88 KB | 0.12 KB | N/A | 179524 | N/A | -| Π_user | 15.88 KB | 0.12 KB | 2972965 | 170392 | 3143357 | -| Π_dec | 15.88 KB | 3.25 KB | N/A | 188220 | N/A | +| Π_DKG | 15.88 KB | 0.12 KB | N/A | 175000 | N/A | +| Π_user | 15.88 KB | 0.12 KB | N/A | 170320 | N/A | +| Π_dec | 15.88 KB | 3.25 KB | N/A | 188232 | N/A | ### Role / Phase / Activity @@ -101,7 +101,7 @@ Single-circuit `bb prove` on the benchmark oracle witness (not the integration a | --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | | Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 29.42 s | 127.00 KB | 128.19 KB | | Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 2.13 s | 15.88 KB | 16.00 KB | -| User | P3 | per user input | isolated_nargo | 0.64 s | 15.88 KB | 16.00 KB | +| User | P3 | per user input | isolated_nargo | 0.67 s | 15.88 KB | 16.00 KB | | Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 0.53 s | 15.88 KB | 16.00 KB | | Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 18.68 s | 15.88 KB | 19.12 KB | | Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 8.47 s | 15.88 KB | 19.12 KB | diff --git a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh index 309e79218..bbaa1ef51 100755 --- a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh +++ b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh @@ -150,14 +150,14 @@ CRISP_TEST_EXIT_CODE=${PIPESTATUS[0]} echo " [gas] CRISP test completed (exit=${CRISP_TEST_EXIT_CODE})." require_preset_artifacts BENCHMARK_PROOF_AGGREGATION="${BENCHMARK_PROOF_AGGREGATION:-true}" -echo " [gas] Running integration test (test_trbfv_actor); proof_aggregation=${BENCHMARK_PROOF_AGGREGATION}..." +echo " [gas] Running integration test (test_trbfv_actor); proof_aggregation=${BENCHMARK_PROOF_AGGREGATION}, multithread_jobs=${BENCHMARK_MULTITHREAD_JOBS:-1}, profile=release..." ( cd "$REPO_ROOT" && \ BENCHMARK_MODE="$MODE" \ BENCHMARK_PROOF_AGGREGATION="$BENCHMARK_PROOF_AGGREGATION" \ BENCHMARK_FOLDED_OUTPUT="$TMP_JSON_FOLDED" \ BENCHMARK_SUMMARY_OUTPUT="$TMP_JSON_SUMMARY" \ - cargo test -p e3-tests test_trbfv_actor -- --nocapture + cargo test --release -p e3-tests test_trbfv_actor -- --nocapture ) 2>&1 | tee "$TMP_LOG_FOLDED" FOLDED_TEST_EXIT_CODE=${PIPESTATUS[0]} echo " [gas] Integration export completed (exit=${FOLDED_TEST_EXIT_CODE})." diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 8e57aee8f..5774b8c0e 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -91,6 +91,8 @@ if [ -n "$MULTITHREAD_JOBS" ]; then exit 1 fi export BENCHMARK_MULTITHREAD_JOBS="$MULTITHREAD_JOBS" +elif [ -n "${BENCHMARK_MULTITHREAD_JOBS:-}" ]; then + echo " Using BENCHMARK_MULTITHREAD_JOBS from environment: $BENCHMARK_MULTITHREAD_JOBS" fi if [ ! -f "$CONFIG_FILE" ]; then diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 469bcc56c..617e36ba1 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -18,10 +18,10 @@ use e3_data::{InMemStore, RepositoriesFactory}; use e3_events::{ AggregateConfig, AggregateId, BusHandle, EnclaveEvent, EventBus, EventBusConfig, EvmEventConfig, }; -use e3_evm::{BondingRegistrySolReader, CiphernodeRegistrySolReader, EnclaveSolWriter}; use e3_evm::{ - CiphernodeRegistrySol, EnclaveSolReader, ProviderConfig, SlashingManagerSolReader, - SlashingManagerSolWriter, + fetch_accusation_vote_validity, fetch_dkg_fold_attestation_verifier, BondingRegistrySolReader, + CiphernodeRegistrySol, CiphernodeRegistrySolReader, EnclaveSolReader, EnclaveSolWriter, + ProviderConfig, SlashingManagerSolReader, SlashingManagerSolWriter, }; use e3_fhe::ext::FheExtension; use e3_keyshare::ext::ThresholdKeyshareExtension; @@ -82,8 +82,6 @@ pub struct CiphernodeBuilder { testmode_signer: Option, threshold_plaintext_agg: bool, zk_backend: Option, - /// Test/benchmark: EIP-712 verifying contract for DKG fold attestations (no RPC required). - dkg_fold_attestation_verifier: Option
, /// Test/benchmark: EIP-712 verifying contract for accusation votes (no RPC required). slashing_manager: Option
, net_config: Option, @@ -154,7 +152,6 @@ impl CiphernodeBuilder { threads: None, testmode_signer: None, threshold_plaintext_agg: false, - dkg_fold_attestation_verifier: None, slashing_manager: None, net_config: None, zk_backend: None, @@ -220,12 +217,6 @@ impl CiphernodeBuilder { self } - /// Benchmark/test: set fold attestation verifier without configuring EVM chains (no RPC). - pub fn testmode_with_dkg_fold_attestation_verifier(mut self, verifier: Address) -> Self { - self.dkg_fold_attestation_verifier = Some(verifier); - self - } - /// Benchmark/test: set slashing manager address (EIP-712 verifyingContract for /// accusation votes) without configuring EVM chains (no RPC). pub fn testmode_with_slashing_manager(mut self, slashing_manager: Address) -> Self { @@ -250,24 +241,75 @@ impl CiphernodeBuilder { }) } - fn resolve_dkg_fold_attestation_verifier(&self) -> Result> { - if let Some(addr) = self.dkg_fold_attestation_verifier { - return Ok(Some(addr)); - } - let resolved = self - .chains - .first() - .and_then(|c| c.contracts.dkg_fold_attestation_verifier.as_ref()) - .map(|c| c.address()) - .transpose()?; - if resolved.is_none() { + /// Fetch `CiphernodeRegistry.dkgFoldAttestationVerifier()` at startup (EIP-712 verifying contract). + async fn fetch_dkg_fold_attestation_verifier_from_registry( + provider_cache: &mut ProviderCache, + chains: &[ChainConfig], + ) -> Result> { + let Some(chain) = chains.iter().find(|c| c.enabled.unwrap_or(true)) else { + return Ok(None); + }; + let provider = provider_cache.ensure_read_provider(chain).await?; + let registry = chain.contracts.ciphernode_registry.address()?; + let verifier = fetch_dkg_fold_attestation_verifier(provider.provider(), registry).await?; + if verifier.is_none() { tracing::warn!( - "`dkg_fold_attestation_verifier` contract address is not set in chain config. \ - If any E3 enables `proof_aggregation_enabled`, this node will fail to sign \ - DKG fold attestations and be counted as dishonest." + registry = %registry, + "CiphernodeRegistry.dkgFoldAttestationVerifier is not set on-chain; \ + nodes will not sign DKG fold attestations when proof aggregation is enabled" + ); + } else if let Some(addr) = verifier { + info!( + registry = %registry, + verifier = %addr, + "loaded dkgFoldAttestationVerifier from CiphernodeRegistry" ); } - Ok(resolved) + Ok(verifier) + } + + /// Fetch `CiphernodeRegistry.accusationVoteValidity()` at startup (off-chain + /// vote freshness window in seconds). Returns `0` when the registry has + /// disabled slashing (governance emergency stop) or when no chain is + /// configured (in-process benchmarks); the actor will then refuse to stamp + /// votes that would be rejected on chain. + /// + /// The `u256` returned by the registry is clamped to `u64`. The contract + /// has no upper bound but `u64::MAX` seconds is already ~5.8 × 10¹¹ years — + /// any value that doesn't fit in `u64` is treated as "effectively infinite" + /// by saturating at `u64::MAX`, matching the on-chain `block.timestamp` + /// comparison. + async fn fetch_accusation_vote_validity_from_registry( + provider_cache: &mut ProviderCache, + chains: &[ChainConfig], + ) -> Result { + let Some(chain) = chains.iter().find(|c| c.enabled.unwrap_or(true)) else { + return Ok(0); + }; + let provider = provider_cache.ensure_read_provider(chain).await?; + let registry = chain.contracts.ciphernode_registry.address()?; + let validity = fetch_accusation_vote_validity(provider.provider(), registry).await?; + let secs = match validity { + Some(v) => { + let clamped: u64 = v.try_into().unwrap_or(u64::MAX); + info!( + registry = %registry, + accusation_vote_validity_secs = clamped, + "loaded accusationVoteValidity from CiphernodeRegistry" + ); + clamped + } + None => { + tracing::warn!( + registry = %registry, + "CiphernodeRegistry.accusationVoteValidity is 0; the off-chain \ + accusation manager will not produce votes (governance-disabled \ + or pre-initialized registry)" + ); + 0 + } + }; + Ok(secs) } /// Log data actor events @@ -307,6 +349,27 @@ impl CiphernodeBuilder { self } + /// Configure the Rayon compute pool for production workloads. + /// + /// Reserves `reserve_threads` CPUs for Actix / networking, uses the remainder for Rayon, and + /// allows up to `concurrent_jobs` CPU-bound tasks at once (ZK + TrBFV). When `concurrent_jobs` + /// is `None`, uses all available compute threads. + pub fn with_multithread_config( + mut self, + reserve_threads: usize, + concurrent_jobs: Option, + ) -> Self { + let max_threads = Multithread::get_max_threads_minus(reserve_threads); + let jobs = concurrent_jobs.unwrap_or(max_threads).max(1); + let pool_threads = jobs.min(max_threads).max(1); + info!( + "Multithread pool: rayon_threads={pool_threads}, max_concurrent_jobs={jobs}, reserve_threads={reserve_threads}" + ); + self.threads = Some(pool_threads); + self.multithread_concurrent_jobs = Some(jobs); + self + } + /// This will save the given number of threads from being used by the rayon threadpool pub fn with_max_threads_minus(mut self, threads: usize) -> Self { self.threads = Some(Multithread::get_max_threads_minus(threads)); @@ -520,6 +583,40 @@ impl CiphernodeBuilder { ) .await?; + let needs_zk_actors = + self.keyshare.is_some() || (self.pubkey_agg && self.keyshare.is_none()); + let dkg_fold_verifier_addr = if needs_zk_actors { + if !self.chains.is_empty() { + Self::fetch_dkg_fold_attestation_verifier_from_registry( + &mut provider_cache, + &self.chains, + ) + .await? + } else { + // In-process benchmark harness (no EVM chains): optional env override. + std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER") + .ok() + .and_then(|s| s.parse().ok()) + } + } else { + None + }; + + // Off-chain freshness window for accusation votes — fetched alongside + // the verifier so AccusationManagerExtension (created below) has + // everything it needs without re-walking the chain. Benchmark harness + // falls back to the env var so deterministic builds don't require + // RPC access. + let accusation_vote_validity_secs = if !self.chains.is_empty() { + Self::fetch_accusation_vote_validity_from_registry(&mut provider_cache, &self.chains) + .await? + } else { + std::env::var("BENCHMARK_ACCUSATION_VOTE_VALIDITY_SECS") + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(0) + }; + // E3 specific setup let mut e3_builder = E3Router::builder(&bus, store.clone()); @@ -543,7 +640,6 @@ impl CiphernodeBuilder { )); info!("Setting up ZK actors"); - let dkg_fold_verifier_addr = self.resolve_dkg_fold_attestation_verifier()?; setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_addr); } @@ -565,7 +661,6 @@ impl CiphernodeBuilder { .ok_or_else(|| anyhow::anyhow!("ZK backend is required for aggregator"))?; let signer = provider_cache.ensure_signer().await?; info!("Setting up ZK actors for aggregator"); - let dkg_fold_verifier_addr = self.resolve_dkg_fold_attestation_verifier()?; setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_addr); } } @@ -582,11 +677,15 @@ impl CiphernodeBuilder { { let signer = provider_cache.ensure_signer().await?; let slashing_manager_addr = self.resolve_slashing_manager()?; - info!("Setting up AccusationManagerExtension"); + info!( + vote_validity_secs = accusation_vote_validity_secs, + "Setting up AccusationManagerExtension" + ); e3_builder = e3_builder.with(AccusationManagerExtension::create( &bus, signer, slashing_manager_addr, + accusation_vote_validity_secs, )); } @@ -651,10 +750,10 @@ impl CiphernodeBuilder { // Setup threadpool if not set let task_pool = self.task_pool.clone().unwrap_or_else(|| { - Multithread::create_taskpool( - self.threads.unwrap_or(1), - self.multithread_concurrent_jobs.unwrap_or(1), - ) + let pool_threads = self.threads.unwrap_or(1); + let concurrent_jobs = self.multithread_concurrent_jobs.unwrap_or(1); + let pool_threads = concurrent_jobs.min(pool_threads).max(1); + Multithread::create_taskpool(pool_threads, concurrent_jobs) }); // Create it with or without ZK prover diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index ef9f06149..b407431b6 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -53,6 +53,16 @@ pub struct NodeDefinition { pub autowallet: bool, /// Optional dashboard port. When set, serves a monitoring web UI on this port. pub dashboard_port: Option, + /// Logical CPUs reserved for Actix, libp2p, and RPC (not used by the Rayon compute pool). + #[serde(default = "default_multithread_reserve_threads")] + pub multithread_reserve_threads: usize, + /// Max concurrent CPU-bound jobs (ZK proofs + TrBFV). When unset, defaults to all CPUs minus + /// `multithread_reserve_threads`. Override with env `E3_NODE__MULTITHREAD_CONCURRENT_JOBS`. + pub multithread_concurrent_jobs: Option, +} + +fn default_multithread_reserve_threads() -> usize { + 1 } impl Default for NodeDefinition { @@ -71,6 +81,8 @@ impl Default for NodeDefinition { autopassword: false, autowallet: false, dashboard_port: None, + multithread_reserve_threads: default_multithread_reserve_threads(), + multithread_concurrent_jobs: None, } } } @@ -389,6 +401,17 @@ impl AppConfig { pub fn dashboard_port(&self) -> Option { self.node_def().dashboard_port } + + /// CPUs reserved for non-compute work (Actix, networking, RPC). + pub fn multithread_reserve_threads(&self) -> usize { + self.node_def().multithread_reserve_threads + } + + /// Optional cap on concurrent ZK / TrBFV pool jobs. When `None`, the node uses all CPUs minus + /// [`Self::multithread_reserve_threads`]. + pub fn multithread_concurrent_jobs(&self) -> Option { + self.node_def().multithread_concurrent_jobs + } } #[derive(Debug, Deserialize, Serialize)] @@ -781,6 +804,25 @@ chains: }); } + #[test] + fn test_multithread_config() -> Result<()> { + let config_str = r#" +node: + multithread_reserve_threads: 2 + multithread_concurrent_jobs: 4 +"#; + let unscoped: UnscopedAppConfig = serde_yaml::from_str(config_str)?; + let config = unscoped.into_scoped_with_defaults( + "_default", + &PathBuf::from("/default/data"), + &PathBuf::from("/default/config"), + &PathBuf::from("/my/cwd"), + )?; + assert_eq!(config.multithread_reserve_threads(), 2); + assert_eq!(config.multithread_concurrent_jobs(), Some(4)); + Ok(()) + } + #[test] fn test_config_env_vars() { Jail::expect_with(|jail| { diff --git a/crates/config/src/contract.rs b/crates/config/src/contract.rs index 7af3277db..32c856d2a 100644 --- a/crates/config/src/contract.rs +++ b/crates/config/src/contract.rs @@ -49,10 +49,6 @@ pub struct ContractAddresses { pub e3_program: Option, pub fee_token: Option, pub slashing_manager: Option, - /// On-chain `DkgFoldAttestationVerifier` address. Required when running with - /// proof aggregation; used as the EIP-712 `verifyingContract` when nodes - /// sign DKG fold attestations. - pub dkg_fold_attestation_verifier: Option, } impl ContractAddresses { @@ -64,7 +60,6 @@ impl ContractAddresses { self.e3_program.as_ref(), self.fee_token.as_ref(), self.slashing_manager.as_ref(), - self.dkg_fold_attestation_verifier.as_ref(), ] .into_iter() .flatten() diff --git a/crates/entrypoint/src/start/start.rs b/crates/entrypoint/src/start/start.rs index 8c35c9063..cadfbddef 100644 --- a/crates/entrypoint/src/start/start.rs +++ b/crates/entrypoint/src/start/start.rs @@ -12,7 +12,7 @@ use e3_zk_prover::ZkBackend; use rand::SeedableRng; use rand_chacha::rand_core::OsRng; use std::sync::{Arc, Mutex}; -use tracing::instrument; +use tracing::{info, instrument}; #[instrument(name = "app", skip_all)] pub async fn execute(config: &AppConfig) -> Result { @@ -20,13 +20,22 @@ pub async fn execute(config: &AppConfig) -> Result { let cipher = Arc::new(Cipher::from_file(&config.key_file()).await?); let backend = ZkBackend::new(config.bb_binary(), config.circuits_dir(), config.work_dir()); + let reserve = config.multithread_reserve_threads(); + let concurrent_jobs = config.multithread_concurrent_jobs(); + info!( + "Ciphernode multithread: reserve_threads={reserve}, concurrent_jobs={}", + concurrent_jobs + .map(|n| n.to_string()) + .unwrap_or_else(|| "auto (CPUs - reserve)".to_string()) + ); + let node = CiphernodeBuilder::new(rng.clone(), cipher.clone()) .with_persistence(&config.log_file(), &config.db_file()) .with_sortition_score() .with_chains(&config.chains()) .with_contract_enclave_full() .with_contract_bonding_registry() - .with_max_threads() + .with_multithread_config(reserve, concurrent_jobs) .with_contract_ciphernode_registry() .with_contract_slashing_manager() .with_trbfv() diff --git a/crates/events/src/enclave_event/accusation_quorum_reached.rs b/crates/events/src/enclave_event/accusation_quorum_reached.rs index d1fe6fd1c..5c2fc77f5 100644 --- a/crates/events/src/enclave_event/accusation_quorum_reached.rs +++ b/crates/events/src/enclave_event/accusation_quorum_reached.rs @@ -15,7 +15,13 @@ use std::fmt::{self, Display}; pub enum AccusationOutcome { /// >= M nodes agree the proof is bad → slash the accused. AccusedFaulted, - /// Only the accuser says bad, same data_hash as others → accuser lied. + /// **Deprecated.** Previously emitted when `votes_against >= M`. The + /// `AccusationVote` gossip wire no longer carries disagreement + /// signatures (a peer who finds the proof passes simply stays silent), + /// so this outcome is no longer produced by the off-chain quorum + /// protocol. Kept in the enum for serialized-event backwards + /// compatibility — downstream consumers should treat any historic + /// `AccuserLied` event the same as `Inconclusive`. AccuserLied, /// data_hashes differ between voters → accused sent different data to different nodes. Equivocation, @@ -49,18 +55,23 @@ pub struct AccusationQuorumReached { /// Which proof type was disputed. pub proof_type: ProofType, /// Votes from nodes that agreed the proof is bad. + /// + /// There is no `votes_against` companion: the gossip protocol no longer + /// carries disagreement signatures, so silence is the only signal of + /// disagreement. See the `AccusationVote` docstring for the rationale. pub votes_for: Vec, - /// Votes from nodes that said the proof is fine. - pub votes_against: Vec, /// The quorum decision. pub outcome: AccusationOutcome, /// Raw `abi.encode(proof.data, public_signals)` — preimage of every voter's - /// `data_hash`. The on-chain `SlashingManager.proposeSlash` recomputes - /// `keccak256(evidence)` and requires it to equal the common voter - /// `dataHash`, binding the votes to specific evidence bytes on-chain. - /// Empty when this node didn't have the raw bytes locally (e.g. consistency- - /// violation path); slashing still works in that case but without the - /// on-chain evidence binding. + /// `data_hash`. + /// + /// **Off-chain audit metadata only.** The current Solidity + /// `SlashingManager.proposeSlash` no longer recomputes + /// `keccak256(evidence)` on chain (it equates voter `dataHash`es directly + /// for equivocation detection). The preimage is kept here so off-chain + /// observers can independently verify what every voter claimed to see + /// without re-fetching the proof from peers. Empty when this node didn't + /// have the raw bytes locally (e.g. consistency-violation path). #[serde(default)] pub evidence: Bytes, } diff --git a/crates/events/src/enclave_event/accusation_vote.rs b/crates/events/src/enclave_event/accusation_vote.rs index c07a49731..510644d1e 100644 --- a/crates/events/src/enclave_event/accusation_vote.rs +++ b/crates/events/src/enclave_event/accusation_vote.rs @@ -11,10 +11,45 @@ use e3_utils::ArcBytes; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display}; -/// Broadcast via gossip: a committee member's vote on an accusation. +/// EIP-712 domain `name` for accusation vote signatures. /// -/// Each committee member independently checks whether the accused's proof -/// failed verification from their perspective, and broadcasts this vote. +/// MUST byte-equal the literal passed to `EIP712(...)` in +/// `packages/enclave-contracts/contracts/slashing/SlashingManager.sol` +/// (`EIP712_DOMAIN_NAME` constant there). Off-chain signers and the on-chain +/// verifier share this one string — diverging here silently breaks +/// `ECDSA.recover` on every slashing submission. +pub const VOTE_DOMAIN_NAME: &str = "EnclaveSlashing"; + +/// EIP-712 domain `version` for accusation vote signatures. Same alignment +/// rule as [`VOTE_DOMAIN_NAME`]. +pub const VOTE_DOMAIN_VERSION: &str = "1"; + +/// EIP-712 struct typehash string for [`AccusationVote`]. +/// +/// MUST byte-equal `SlashingManager.VOTE_TYPEHASH`'s source string. Reordering +/// or renaming fields here without updating Solidity (or vice versa) silently +/// breaks signature recovery on chain. +pub const VOTE_TYPEHASH_STR: &str = + "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bytes32 dataHash,uint256 deadline)"; + +/// Broadcast via gossip: a committee member's vote agreeing with an accusation. +/// +/// A node broadcasts an `AccusationVote` only when its own local verification +/// of the disputed proof also failed. There is no "disagree" vote on the +/// wire — a peer who finds the proof passes simply stays silent. This matches +/// the on-chain `SlashingManager._verifyAttestationEvidence`, which consumes +/// only agreeing signatures and treats every submitted vote as an +/// affirmative attestation; carrying an explicit `agrees` flag here would be +/// dead bytes off-chain and unverifiable gossip metadata (a malicious peer +/// could flip the flag in transit without invalidating the EIP-712 signature +/// over the on-chain digest). +/// +/// **Loss of fast-fail.** Off-chain quorum protocols sometimes track explicit +/// "no" votes so an accusation that clearly cannot reach quorum exits the +/// pending pool early. We trade that optimization for protocol simplicity and +/// soundness: an unanswerable accusation now runs to `vote_timeout` (default 5 min) +/// before being declared inconclusive. Other committee members' silence is the +/// signal; no separate signed message is required. #[derive(Message, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[rtype(result = "()")] pub struct AccusationVote { @@ -23,11 +58,13 @@ pub struct AccusationVote { pub accusation_id: [u8; 32], /// Ethereum address of the voter. pub voter: Address, - /// `true` if this node also saw the proof fail verification. - pub agrees: bool, /// keccak256 hash of the data as this node received it — for equivocation detection. pub data_hash: [u8; 32], - /// ECDSA signature of the voter over the vote fields. + /// Unix-seconds deadline shared across all voters for this accusation. + /// Bound into the EIP-712 vote digest and re-checked on-chain by + /// `SlashingManager._verifyAttestationEvidence` via `block.timestamp <= deadline`. + pub deadline: u64, + /// ECDSA signature of the voter over the EIP-712 vote digest. pub signature: ArcBytes, } @@ -35,8 +72,8 @@ impl Display for AccusationVote { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "AccusationVote {{ e3_id: {}, voter: {}, agrees: {} }}", - self.e3_id, self.voter, self.agrees + "AccusationVote {{ e3_id: {}, voter: {} }}", + self.e3_id, self.voter ) } } diff --git a/crates/events/src/enclave_event/proof_failure_accusation.rs b/crates/events/src/enclave_event/proof_failure_accusation.rs index 185b5830e..f965ccead 100644 --- a/crates/events/src/enclave_event/proof_failure_accusation.rs +++ b/crates/events/src/enclave_event/proof_failure_accusation.rs @@ -33,6 +33,11 @@ pub struct ProofFailureAccusation { pub proof_type: ProofType, /// keccak256 hash of (data + proof) as received by the accuser. pub data_hash: [u8; 32], + /// Unix-seconds deadline after which votes signed for this accusation must be + /// rejected on-chain (`SlashingManager._verifyAttestationEvidence` enforces + /// `block.timestamp <= deadline`). Set by the accuser; every voter signs the + /// same value so the aggregated evidence carries one shared deadline. + pub deadline: u64, /// For C3a/C3b: the signed proof payload so other nodes can re-verify. /// `None` for proofs that all nodes already received. pub signed_payload: Option, diff --git a/crates/evm/src/ciphernode_registry_sol.rs b/crates/evm/src/ciphernode_registry_sol.rs index 4bd5d2941..95b542fd3 100644 --- a/crates/evm/src/ciphernode_registry_sol.rs +++ b/crates/evm/src/ciphernode_registry_sol.rs @@ -725,6 +725,54 @@ pub async fn publish_committee_to_registry( + provider: &P, + registry_address: Address, +) -> Result> { + sol! { + #[sol(rpc)] + interface ICiphernodeRegistryDkgFoldView { + function dkgFoldAttestationVerifier() external view returns (address); + } + } + + let contract = ICiphernodeRegistryDkgFoldView::new(registry_address, provider); + let verifier = contract.dkgFoldAttestationVerifier().call().await?; + if verifier == Address::ZERO { + Ok(None) + } else { + Ok(Some(verifier)) + } +} + +/// Read `CiphernodeRegistry.accusationVoteValidity()` — registry-wide off-chain +/// freshness window (seconds) accusers stamp on `AccusationVote.deadline`. +/// Returns the raw `uint256` as `U256`; callers decide how to clamp it to +/// their own arithmetic type. `Ok(None)` is reserved for the case where the +/// registry has been governance-disabled (`accusationVoteValidity = 0`) so +/// the caller can short-circuit without producing votes that will never +/// verify on chain. +pub async fn fetch_accusation_vote_validity( + provider: &P, + registry_address: Address, +) -> Result> { + sol! { + #[sol(rpc)] + interface ICiphernodeRegistryAccusationVoteView { + function accusationVoteValidity() external view returns (uint256); + } + } + + let contract = ICiphernodeRegistryAccusationVoteView::new(registry_address, provider); + let validity = contract.accusationVoteValidity().call().await?; + if validity.is_zero() { + Ok(None) + } else { + Ok(Some(validity)) + } +} + /// Wrapper for a reader and writer pub struct CiphernodeRegistrySol; diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index 1509076b7..9332379eb 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -26,7 +26,8 @@ mod sync_start_extractor; pub use bonding_registry_sol::BondingRegistrySolReader; pub use ciphernode_registry_sol::{ - CiphernodeRegistrySol, CiphernodeRegistrySolReader, CiphernodeRegistrySolWriter, + fetch_accusation_vote_validity, fetch_dkg_fold_attestation_verifier, CiphernodeRegistrySol, + CiphernodeRegistrySolReader, CiphernodeRegistrySolWriter, }; pub use dkg_attestation_bundle::encode_dkg_attestation_bundle; pub use enclave_sol_reader::EnclaveSolReader; @@ -41,5 +42,5 @@ pub use fix_historical_order::*; pub use helpers::*; pub use repo::*; pub use slashing_manager_sol_reader::SlashingManagerSolReader; -pub use slashing_manager_sol_writer::SlashingManagerSolWriter; +pub use slashing_manager_sol_writer::{encode_attestation_evidence, SlashingManagerSolWriter}; pub use sync_start_extractor::*; diff --git a/crates/evm/src/slashing_manager_sol_writer.rs b/crates/evm/src/slashing_manager_sol_writer.rs index a709407f6..e979422da 100644 --- a/crates/evm/src/slashing_manager_sol_writer.rs +++ b/crates/evm/src/slashing_manager_sol_writer.rs @@ -194,35 +194,35 @@ impl Handler /// Encode `AccusationQuorumReached` into the attestation evidence format expected /// by `SlashingManager.proposeSlash()`: -/// `abi.encode(uint256 proofType, address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures, bytes evidence)` +/// `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, uint256 deadline, bytes[] signatures)` /// -/// Voters are sorted ascending by address to satisfy the contract's duplicate-prevention check. -/// `evidence` is the `abi.encode(proof.data, public_signals)` preimage of `dataHash` — the contract -/// recomputes `keccak256(evidence)` and requires it to equal the common voter `dataHash`. -fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Vec { +/// Voters are sorted ascending by address to satisfy the contract's duplicate-prevention +/// check. All `votes_for` share the same `deadline` (the accuser stamps one value at +/// accusation time and `AccusationManager::on_vote_received` rejects votes whose +/// deadline disagrees), so the encoder pulls it from the first vote. Returns `None` +/// if `votes_for` is empty — the on-chain submitter must skip the submission in that +/// case rather than send malformed calldata. +pub fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Option> { + if data.votes_for.is_empty() { + return None; + } + // Collect and sort votes by voter address (ascending) let mut votes = data.votes_for.clone(); votes.sort_by_key(|v| v.voter); let proof_type = U256::from(data.proof_type as u8); let voters: Vec
= votes.iter().map(|v| v.voter).collect(); - let agrees: Vec = votes.iter().map(|v| v.agrees).collect(); let data_hashes: Vec<[u8; 32]> = votes.iter().map(|v| v.data_hash).collect(); + // All voters signed the same deadline (enforced off-chain by AccusationManager); + // pick any one — the first vote suffices. + let deadline = U256::from(votes[0].deadline); let signatures: Vec = votes .iter() .map(|v| Bytes::from(v.signature.extract_bytes())) .collect(); - let evidence: Bytes = data.evidence.clone(); - ( - proof_type, - voters, - agrees, - data_hashes, - signatures, - evidence, - ) - .abi_encode_params() + Some((proof_type, voters, data_hashes, deadline, signatures).abi_encode_params()) } async fn submit_slash_proposal( @@ -233,7 +233,30 @@ async fn submit_slash_proposal( let e3_id: U256 = data.e3_id.clone().try_into()?; let operator = data.accused; - let proof_data = encode_attestation_evidence(&data); + // Empty `votes_for` only reaches this point if upstream invariants broke + // — `check_quorum` requires `len >= threshold_m >= 1` before emitting + // `AccusedFaulted`/`Equivocation`. Refuse to submit malformed calldata + // and surface a structured warning so an operator can debug the upstream + // gossip/quorum path rather than seeing a generic ABI-decode revert + // on chain. + let proof_data = match encode_attestation_evidence(&data) { + Some(bytes) => bytes, + None => { + warn!( + e3_id = %data.e3_id, + accused = %operator, + outcome = %data.outcome, + "Refusing to submit proposeSlash: AccusationQuorumReached carries no \ + agreeing votes — upstream quorum invariant violated, dropping submission" + ); + return Err(anyhow::anyhow!( + "AccusationQuorumReached has empty votes_for; refused proposeSlash submission \ + (e3_id={}, accused={})", + data.e3_id, + operator + )); + } + }; send_tx_with_retry("proposeSlash", &[], || { info!("proposeSlash() e3_id={:?} operator={:?}", e3_id, operator); diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 2e0b528a5..d3f1cce85 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -534,12 +534,16 @@ fn handle_trbfv_request( request: ComputeRequest, id: u8, ) -> (Result, Duration) { + // Hold one RNG lock for the whole TrBFV job so overlapping pool tasks do not interleave draws. + let mut rng_guard = rng.lock().expect("SharedRng mutex poisoned"); + let rng_mut = &mut *rng_guard; + match trbfv_req { TrBFVRequest::GenPkShareAndSkSss(req) => { timefunc( "gen_pk_share_and_sk_sss", id, - || match gen_pk_share_and_sk_sss(&rng, &cipher, req) { + || match gen_pk_share_and_sk_sss(rng_mut, &cipher, req) { Ok(o) => Ok(ComputeResponse::trbfv( TrBFVResponse::GenPkShareAndSkSss(o), request.correlation_id, @@ -555,7 +559,7 @@ fn handle_trbfv_request( ) } TrBFVRequest::GenEsiSss(req) => timefunc("gen_esi_sss", id, || { - match gen_esi_sss(&rng, &cipher, req) { + match gen_esi_sss(rng_mut, &cipher, req) { Ok(o) => Ok(ComputeResponse::trbfv( TrBFVResponse::GenEsiSss(o), request.correlation_id, diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index 522ebda66..aa552deeb 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -99,6 +99,15 @@ pub fn create_shared_rng_from_u64(value: u64) -> Arc1` +/// can run `GenPkShare` / `GenEsiSss` in parallel. +pub fn derive_shared_rng(base_seed: u64, salt: u64) -> SharedRng { + create_shared_rng_from_u64(base_seed.wrapping_add(salt)) +} + pub fn create_seed_from_u64(value: u64) -> Seed { Seed(ChaCha20Rng::seed_from_u64(value).get_seed()) } diff --git a/crates/test-helpers/src/usecase_helpers.rs b/crates/test-helpers/src/usecase_helpers.rs index 467d12696..f844838eb 100644 --- a/crates/test-helpers/src/usecase_helpers.rs +++ b/crates/test-helpers/src/usecase_helpers.rs @@ -65,25 +65,31 @@ pub fn generate_shares_hash_map( pk_share, e_sm_raw, .. - } = gen_pk_share_and_sk_sss( - &rng, - &cipher, - GenPkShareAndSkSssRequest { - trbfv_config: trbfv_config.clone(), - crp: ArcBytes::from_bytes(&crp.to_bytes()), - lambda: 40, - num_ciphertexts: 1, - }, - )?; - - let GenEsiSssResponse { esi_sss } = gen_esi_sss( - &rng, - &cipher, - GenEsiSssRequest { - trbfv_config: trbfv_config.clone(), - e_sm_raw: e_sm_raw.clone(), - }, - )?; + } = { + let mut rng_guard = rng.lock().unwrap(); + gen_pk_share_and_sk_sss( + &mut *rng_guard, + &cipher, + GenPkShareAndSkSssRequest { + trbfv_config: trbfv_config.clone(), + crp: ArcBytes::from_bytes(&crp.to_bytes()), + lambda: 40, + num_ciphertexts: 1, + }, + ) + }?; + + let GenEsiSssResponse { esi_sss } = { + let mut rng_guard = rng.lock().unwrap(); + gen_esi_sss( + &mut *rng_guard, + &cipher, + GenEsiSssRequest { + trbfv_config: trbfv_config.clone(), + e_sm_raw: e_sm_raw.clone(), + }, + )? + }; // Decrypt locally stored secrets let decrypted_sk_sss: SharedSecret = sk_sss.decrypt(&cipher)?; diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index b30003cc5..7dfbdc57f 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -32,7 +32,7 @@ use e3_test_helpers::ciphernode_system::{ CiphernodeHistory, CiphernodeSystem, CiphernodeSystemBuilder, }; use e3_test_helpers::{ - create_seed_from_u64, create_shared_rng_from_u64, find_bb, with_tracing, AddToCommittee, + create_seed_from_u64, derive_shared_rng, find_bb, with_tracing, AddToCommittee, }; use e3_trbfv::helpers::calculate_error_size; use e3_trbfv::{TrBFVRequest, TrBFVResponse}; @@ -50,6 +50,7 @@ use rand::rngs::OsRng; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use std::ffi::OsString; +use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{Duration, Instant}; use std::{fs, path::PathBuf, sync::Arc}; use tokio::{ @@ -142,14 +143,23 @@ fn benchmark_multithread_concurrent_jobs() -> usize { .unwrap_or(1) } -/// Fold attestation verifier for benchmarks (no live RPC; used only for EIP-712 signing). +static NEXT_BENCHMARK_NODE_RNG_SALT: AtomicU64 = AtomicU64::new(1); + +/// One ChaCha20 mutex per ciphernode in `test_trbfv_actor` (see `derive_shared_rng`). +fn next_benchmark_node_rng(base_seed: u64) -> e3_utils::SharedRng { + let salt = NEXT_BENCHMARK_NODE_RNG_SALT.fetch_add(1, Ordering::Relaxed); + derive_shared_rng(base_seed, salt) +} + +/// Fold attestation verifier address for benchmark JSON reports (env override or default). fn benchmark_dkg_fold_attestation_verifier_address() -> Option
{ if !benchmark_proof_aggregation_enabled() { return None; } - let addr = std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER") - .unwrap_or_else(|_| "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0".to_string()); - addr.parse().ok() + std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER") + .ok() + .and_then(|s| s.parse().ok()) + .or_else(|| "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0".parse().ok()) } /// Slashing manager address for benchmarks (no live RPC; used as EIP-712 @@ -1098,8 +1108,7 @@ async fn test_trbfv_actor() -> Result<()> { let setup = Instant::now(); - // Create rng - let rng = create_shared_rng_from_u64(42); + const BENCHMARK_NODE_RNG_BASE: u64 = 42; // Create "trigger" bus let system = EventSystem::new().with_fresh_bus(); @@ -1150,11 +1159,20 @@ async fn test_trbfv_actor() -> Result<()> { // Actor system setup let concurrent_jobs = benchmark_multithread_concurrent_jobs(); - let dkg_fold_verifier = benchmark_dkg_fold_attestation_verifier_address(); + if benchmark_proof_aggregation_enabled() + && std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER").is_err() + { + // In-process benchmark has no RPC; same default as pre-registry-fetch harness. + std::env::set_var( + "BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER", + "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", + ); + } let slashing_manager_addr = benchmark_slashing_manager_address(); let max_threadroom = Multithread::get_max_threads_minus(1); - let task_pool = Multithread::create_taskpool(max_threadroom, concurrent_jobs); - let multithread_report = MultithreadReport::new(max_threadroom, concurrent_jobs).start(); + let pool_threads = concurrent_jobs.min(max_threadroom).max(1); + let task_pool = Multithread::create_taskpool(pool_threads, concurrent_jobs); + let multithread_report = MultithreadReport::new(pool_threads, concurrent_jobs).start(); // Setup ZK backend for proof generation/verification let (zk_backend, _zk_temp) = setup_test_zk_backend(benchmark_params.preset_subdir).await?; @@ -1164,10 +1182,11 @@ async fn test_trbfv_actor() -> Result<()> { // Node 0 stays an observer only because it is excluded from sortition registration. // Adding 20 total nodes: 3 for committee + 3 buffer = 6 selected, 14 unselected .add_group(1, || async { - let addr = rand_eth_addr(&rng); + let node_rng = next_benchmark_node_rng(BENCHMARK_NODE_RNG_BASE); + let addr = rand_eth_addr(&node_rng); println!("Building collector {}!", addr); { - let mut b = CiphernodeBuilder::new(rng.clone(), cipher.clone()) + let mut b = CiphernodeBuilder::new(node_rng, cipher.clone()) .testmode_with_history() .with_shared_taskpool(&task_pool) .with_multithread_concurrent_jobs(concurrent_jobs) @@ -1182,17 +1201,15 @@ async fn test_trbfv_actor() -> Result<()> { .testmode_ignore_address_check() .testmode_with_slashing_manager(slashing_manager_addr) .with_logging(); - if let Some(verifier) = dkg_fold_verifier { - b = b.testmode_with_dkg_fold_attestation_verifier(verifier); - } b.build().await } }) .add_group(19, || async { - let addr = rand_eth_addr(&rng); + let node_rng = next_benchmark_node_rng(BENCHMARK_NODE_RNG_BASE); + let addr = rand_eth_addr(&node_rng); println!("Building normal {}", &addr); { - let mut b = CiphernodeBuilder::new(rng.clone(), cipher.clone()) + let mut b = CiphernodeBuilder::new(node_rng, cipher.clone()) .testmode_with_history() .with_shared_taskpool(&task_pool) .with_multithread_concurrent_jobs(concurrent_jobs) @@ -1207,9 +1224,6 @@ async fn test_trbfv_actor() -> Result<()> { .testmode_ignore_address_check() .testmode_with_slashing_manager(slashing_manager_addr) .with_logging(); - if let Some(verifier) = dkg_fold_verifier { - b = b.testmode_with_dkg_fold_attestation_verifier(verifier); - } b.build().await } }) @@ -1263,7 +1277,7 @@ async fn test_trbfv_actor() -> Result<()> { let proof_aggregation_enabled = benchmark_proof_aggregation_enabled(); println!( - "Benchmark trbfv: proof_aggregation={proof_aggregation_enabled}, preset={}, multithread_jobs={concurrent_jobs}", + "Benchmark trbfv: proof_aggregation={proof_aggregation_enabled}, preset={}, pool_threads={pool_threads}, max_concurrent_jobs={concurrent_jobs}", benchmark_params.preset_subdir ); diff --git a/crates/trbfv/src/gen_esi_sss.rs b/crates/trbfv/src/gen_esi_sss.rs index 213d15e81..e9950d449 100644 --- a/crates/trbfv/src/gen_esi_sss.rs +++ b/crates/trbfv/src/gen_esi_sss.rs @@ -11,8 +11,9 @@ use crate::{ }; use anyhow::{Context, Result}; use e3_crypto::{Cipher, SensitiveBytes}; -use e3_utils::{utility_types::ArcBytes, SharedRng}; +use e3_utils::utility_types::ArcBytes; use fhe::trbfv::ShareManager; +use rand::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use tracing::info; @@ -68,8 +69,8 @@ struct InnerResponse { /// When implementing multiple ciphertext outputs decryptions, we are going to need multiple smudging noise polynomials, /// so we are generating a vector of smudging noise secret shares (esi_sss) instead of just one in anticipation of that change. /// We will also need to ensure that all of them are committed to the pk_generation circuit. -pub fn gen_esi_sss( - rng: &SharedRng, +pub fn gen_esi_sss( + rng: &mut R, cipher: &Cipher, req: GenEsiSssRequest, ) -> Result { @@ -89,7 +90,7 @@ pub fn gen_esi_sss( let esi_sss = vec![SharedSecret::from( share_manager - .generate_secret_shares_from_poly(e_sm_poly.into(), &mut *rng.lock().unwrap()) + .generate_secret_shares_from_poly(e_sm_poly.into(), rng) .context("Failed to generate secret shares from poly")?, )]; diff --git a/crates/trbfv/src/gen_pk_share_and_sk_sss.rs b/crates/trbfv/src/gen_pk_share_and_sk_sss.rs index 06144baab..01377b8a7 100644 --- a/crates/trbfv/src/gen_pk_share_and_sk_sss.rs +++ b/crates/trbfv/src/gen_pk_share_and_sk_sss.rs @@ -12,13 +12,14 @@ use crate::{ }; use anyhow::Result; use e3_crypto::{Cipher, SensitiveBytes}; -use e3_utils::{utility_types::ArcBytes, SharedRng}; +use e3_utils::utility_types::ArcBytes; use fhe::{ bfv::SecretKey, mbfv::{CommonRandomPoly, PublicKeyShare}, trbfv::{ShareManager, TRBFV}, }; use fhe_traits::Serialize as FheSerialize; +use rand::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use std::ops::Deref; use tracing::info; @@ -105,8 +106,8 @@ struct InnerResponse { pub e_sm_raw: ArcBytes, } -pub fn gen_pk_share_and_sk_sss( - rng: &SharedRng, +pub fn gen_pk_share_and_sk_sss( + rng: &mut R, cipher: &Cipher, req: GenPkShareAndSkSssRequest, ) -> Result { @@ -122,9 +123,8 @@ pub fn gen_pk_share_and_sk_sss( "gen_pk_share_and_sk_sss: n={}, t={}", num_ciphernodes, threshold ); - let sk_share = { SecretKey::random(¶ms, &mut *rng.lock().unwrap()) }; - let (pk0_share, _, _, eek) = - { PublicKeyShare::new_extended(&sk_share, crp.clone(), &mut *rng.lock().unwrap())? }; + let sk_share = SecretKey::random(¶ms, rng); + let (pk0_share, _, _, eek) = PublicKeyShare::new_extended(&sk_share, crp.clone(), rng)?; let pk_share = PublicKeyShare::deserialize(&pk0_share.to_bytes(), ¶ms, crp.clone())?; @@ -132,11 +132,7 @@ pub fn gen_pk_share_and_sk_sss( let trbfv = TRBFV::new(num_ciphernodes as usize, threshold as usize, params.clone())?; let share_manager_for_esm = ShareManager::new(num_ciphernodes as usize, threshold as usize, params.clone()); - let esi_coeffs = trbfv.generate_smudging_error( - req.num_ciphertexts, - req.lambda, - &mut *rng.lock().unwrap(), - )?; + let esi_coeffs = trbfv.generate_smudging_error(req.num_ciphertexts, req.lambda, rng)?; let e_sm_rns = share_manager_for_esm.bigints_to_poly(&esi_coeffs)?; let e_sm_raw = ArcBytes::from_bytes(&e_sm_rns.deref().to_bytes()); @@ -150,9 +146,8 @@ pub fn gen_pk_share_and_sk_sss( let sk_raw = ArcBytes::from_bytes(&sk_poly.to_bytes()); info!("gen_pk_share_and_sk_sss:generate_secret_shares_from_poly..."); - let sk_sss = SharedSecret::from({ - share_manager.generate_secret_shares_from_poly(sk_poly, &mut *rng.lock().unwrap())? - }); + let sk_sss = + SharedSecret::from({ share_manager.generate_secret_shares_from_poly(sk_poly, rng)? }); ( InnerResponse { diff --git a/crates/zk-prover/Cargo.toml b/crates/zk-prover/Cargo.toml index 0f8e9b913..d1c972f2a 100644 --- a/crates/zk-prover/Cargo.toml +++ b/crates/zk-prover/Cargo.toml @@ -48,6 +48,7 @@ walkdir = "2.5" [dev-dependencies] e3-test-helpers = { workspace = true } +e3-evm = { workspace = true } ark-bn254 = { workspace = true } ark-ff = { workspace = true } fhe-traits = { workspace = true } diff --git a/crates/zk-prover/src/actors/accusation_manager.rs b/crates/zk-prover/src/actors/accusation_manager.rs index 63fcf13f8..07bb2492f 100644 --- a/crates/zk-prover/src/actors/accusation_manager.rs +++ b/crates/zk-prover/src/actors/accusation_manager.rs @@ -27,7 +27,8 @@ //! | C7 | On-chain verification | Not handled here (on-chain verifier) | use std::collections::{HashMap, HashSet}; -use std::time::Duration; +use std::sync::Arc; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use actix::{Actor, Addr, AsyncContext, Context, Handler, SpawnHandle}; use alloy::primitives::{keccak256, Address, Bytes, U256}; @@ -40,7 +41,8 @@ use e3_events::{ ComputeResponseKind, CorrelationId, E3id, EnclaveEvent, EnclaveEventData, EventContext, EventPublisher, EventSubscriber, EventType, PartyProofsToVerify, ProofFailureAccusation, ProofType, ProofVerificationFailed, ProofVerificationPassed, Sequenced, SignedProofPayload, - SlashExecuted, TypedEvent, VerifyShareProofsRequest, ZkRequest, ZkResponse, + SlashExecuted, TypedEvent, VerifyShareProofsRequest, ZkRequest, ZkResponse, VOTE_DOMAIN_NAME, + VOTE_DOMAIN_VERSION, VOTE_TYPEHASH_STR, }; use e3_utils::{ArcBytes, NotifySync}; use tracing::{error, info, warn}; @@ -48,11 +50,39 @@ use tracing::{error, info, warn}; /// How long to wait for votes before declaring the accusation inconclusive. const DEFAULT_VOTE_TIMEOUT: Duration = Duration::from_secs(300); // 5 minutes -/// An active accusation awaiting votes from committee members. +/// Abstraction over wall-clock time so the deadline-stamping logic is +/// deterministically testable. Production uses [`SystemClock`], which reads +/// `SystemTime::now()`; tests can inject a mock clock that returns fixed +/// timestamps. +pub trait Clock: Send + Sync + 'static { + /// Current Unix time in seconds. Returns `0` if the platform clock is + /// pre-`UNIX_EPOCH` (a broken clock should not silently produce + /// signatures that look valid forever — the on-chain check will then + /// reject the resulting deadline immediately). + fn unix_now_secs(&self) -> u64; +} + +/// Production clock backed by `SystemTime::now()`. +pub struct SystemClock; + +impl Clock for SystemClock { + fn unix_now_secs(&self) -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_secs()) + .unwrap_or(0) + } +} + +/// An active accusation awaiting agreement votes from committee members. +/// +/// There is no `votes_against` field: a peer who finds the disputed proof +/// passes simply stays silent rather than broadcasting a signed disagreement +/// (see `AccusationVote` docstring for rationale). The accusation runs to +/// quorum or to `vote_timeout`. struct PendingAccusation { accusation: ProofFailureAccusation, votes_for: Vec, - votes_against: Vec, /// Handle to the timeout future so it can be cancelled on early quorum. timeout_handle: Option, /// The EventContext from when this accusation was created — used for timeout emission. @@ -146,11 +176,25 @@ pub struct AccusationManager { /// Vote timeout duration. vote_timeout: Duration, + /// Registry-wide off-chain freshness window (seconds) applied when stamping + /// `AccusationVote.deadline`. Fetched once per process from + /// `CiphernodeRegistry.accusationVoteValidity()` so a governance change + /// requires a node restart to take effect — same lifecycle as the fold + /// attestation verifier. + vote_validity_secs: u64, + + /// Wall-clock source used to derive accusation deadlines. Production uses + /// [`SystemClock`]; tests can inject a deterministic mock. + clock: Arc, + /// BFV preset for circuit artifact resolution. params_preset: e3_fhe_params::BfvPreset, } impl AccusationManager { + /// Construct an actor with the production [`SystemClock`]. Use + /// [`AccusationManager::new_with_clock`] in tests that need deterministic + /// timestamps. pub fn new( bus: &BusHandle, e3_id: E3id, @@ -158,7 +202,34 @@ impl AccusationManager { slashing_manager: Address, committee: Vec
, threshold_m: usize, + vote_validity_secs: u64, params_preset: e3_fhe_params::BfvPreset, + ) -> Self { + Self::new_with_clock( + bus, + e3_id, + signer, + slashing_manager, + committee, + threshold_m, + vote_validity_secs, + params_preset, + Arc::new(SystemClock), + ) + } + + /// Construct an actor with an explicit [`Clock`]. Allows unit tests to + /// drive deadline computation without touching wall-clock time. + pub fn new_with_clock( + bus: &BusHandle, + e3_id: E3id, + signer: PrivateKeySigner, + slashing_manager: Address, + committee: Vec
, + threshold_m: usize, + vote_validity_secs: u64, + params_preset: e3_fhe_params::BfvPreset, + clock: Arc, ) -> Self { let my_address = signer.address(); Self { @@ -175,6 +246,8 @@ impl AccusationManager { buffered_votes: HashMap::new(), pending_reverifications: HashMap::new(), vote_timeout: DEFAULT_VOTE_TIMEOUT, + vote_validity_secs, + clock, params_preset, } } @@ -186,6 +259,7 @@ impl AccusationManager { slashing_manager: Address, committee: Vec
, threshold_m: usize, + vote_validity_secs: u64, params_preset: e3_fhe_params::BfvPreset, ) -> Addr { let addr = Self::new( @@ -195,6 +269,7 @@ impl AccusationManager { slashing_manager, committee, threshold_m, + vote_validity_secs, params_preset, ) .start(); @@ -212,6 +287,26 @@ impl AccusationManager { addr } + // ─── Deadline computation ──────────────────────────────────────────── + + /// Compute the on-chain vote-validity deadline (Unix seconds) the accuser + /// stamps on a fresh accusation. Voters then sign this exact value so the + /// aggregated evidence carries one shared deadline that `SlashingManager` + /// checks via `block.timestamp <= deadline`. + /// + /// `vote_validity_secs` is the registry-wide window fetched from + /// `CiphernodeRegistry.accusationVoteValidity()` at process startup — + /// governance can shorten or extend it; live nodes only pick up the new + /// value on restart. + /// + /// `saturating_add` guards against `u64` overflow in the unlikely event + /// governance sets the validity to a near-`u64::MAX` value. + fn compute_deadline(&self) -> u64 { + self.clock + .unix_now_secs() + .saturating_add(self.vote_validity_secs) + } + // ─── Accusation ID computation ─────────────────────────────────────── /// Compute a deterministic ID for an accusation based on its key fields. @@ -247,12 +342,14 @@ impl AccusationManager { /// Structured digest for ECDSA signing of accusations. /// - /// Uses a typehash + `abi.encode` pattern matching `ProofPayload::digest()`: + /// Off-chain only — this digest never reaches the chain. Includes `deadline` + /// so peers can verify the accuser's chosen on-chain validity window has not + /// been tampered with in transit: /// ```text /// keccak256(abi.encode( /// ACCUSATION_TYPEHASH, /// chainId, e3Id, accuser, accused, proofType, - /// dataHash + /// dataHash, deadline /// )) /// ``` fn accusation_digest(accusation: &ProofFailureAccusation) -> [u8; 32] { @@ -262,7 +359,7 @@ impl AccusationManager { .try_into() .expect("E3id should be valid U256"); let typehash: [u8; 32] = keccak256( - "ProofFailureAccusation(uint256 chainId,uint256 e3Id,address accuser,address accused,uint256 proofType,bytes32 dataHash)" + "ProofFailureAccusation(uint256 chainId,uint256 e3Id,address accuser,address accused,uint256 proofType,bytes32 dataHash,uint256 deadline)" ).into(); let encoded = ( typehash, @@ -272,6 +369,7 @@ impl AccusationManager { accusation.accused, U256::from(accusation.proof_type as u8), accusation.data_hash, + U256::from(accusation.deadline), ) .abi_encode(); keccak256(&encoded).into() @@ -291,6 +389,7 @@ impl AccusationManager { } } + #[cfg_attr(test, allow(dead_code))] fn sign_vote_digest(&self, vote: &AccusationVote) -> Result, alloy::signers::Error> { let digest = Self::vote_digest(vote, self.slashing_manager); // `sign_hash_sync` signs the raw 32-byte hash without EIP-191 wrapping, @@ -301,16 +400,19 @@ impl AccusationManager { } /// Canonical EIP-712 domain separator for vote signatures. - /// Must match `SlashingManager`'s domain construction: - /// `keccak256(abi.encode(EIP712_DOMAIN_TYPEHASH, DOMAIN_NAME_HASH, DOMAIN_VERSION_HASH, - /// chainId, verifyingContract))`. + /// + /// Must match `SlashingManager`'s domain construction exactly. The `name` + /// literal is `EIP712_DOMAIN_NAME` in the Solidity contract (see + /// `packages/enclave-contracts/contracts/slashing/SlashingManager.sol`); + /// keep these two strings in lockstep — divergence silently breaks + /// `ECDSA.recover` on chain. fn vote_domain_separator(chain_id: u64, verifying_contract: Address) -> [u8; 32] { let domain_typehash: [u8; 32] = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", ) .into(); - let name_hash: [u8; 32] = keccak256("EnclaveSlashingManager").into(); - let version_hash: [u8; 32] = keccak256("1").into(); + let name_hash: [u8; 32] = keccak256(VOTE_DOMAIN_NAME).into(); + let version_hash: [u8; 32] = keccak256(VOTE_DOMAIN_VERSION).into(); let encoded = ( domain_typehash, name_hash, @@ -323,25 +425,37 @@ impl AccusationManager { } /// Canonical EIP-712 typed-data hash for a vote. - /// `keccak256("\x19\x01" || domainSeparator || structHash)` where - /// `structHash = keccak256(abi.encode(VOTE_TYPEHASH, e3Id, accusationId, voter, agrees, dataHash))`. - fn vote_digest(vote: &AccusationVote, verifying_contract: Address) -> [u8; 32] { + /// + /// `keccak256("\x19\x01" || domainSeparator || structHash)` where the struct + /// matches `SlashingManager.VOTE_TYPEHASH`: + /// `AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bytes32 dataHash,uint256 deadline)`. + /// + /// `AccusationVote` no longer carries an `agrees` field. The gossip wire + /// transmits only agreements; the on-chain verifier treats every submitted + /// signature as an affirmative vote. See the struct's docstring in + /// `e3_events::accusation_vote` for rationale. + /// + /// Exposed `pub` so the Anvil parity test in + /// `crates/zk-prover/tests/slashing_integration_tests.rs` can sign votes + /// through the **same** code path the production actor uses — if the + /// digest drifts from on-chain `_verifyVotes`, the parity test reverts on + /// chain immediately rather than allowing the actor to ship broken + /// signatures. + pub fn vote_digest(vote: &AccusationVote, verifying_contract: Address) -> [u8; 32] { let e3_id_u256: U256 = vote .e3_id .clone() .try_into() .expect("E3id should be valid U256"); - let typehash: [u8; 32] = keccak256( - "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)" - ).into(); + let typehash: [u8; 32] = keccak256(VOTE_TYPEHASH_STR).into(); let struct_hash: [u8; 32] = keccak256( &( typehash, e3_id_u256, vote.accusation_id, vote.voter, - vote.agrees, vote.data_hash, + U256::from(vote.deadline), ) .abi_encode(), ) @@ -531,6 +645,11 @@ impl AccusationManager { return; } + // Pick the on-chain validity deadline once per accusation. Every voter + // (including ourselves below) signs the same value; otherwise the + // aggregated evidence cannot be encoded as a single `deadline`. + let deadline = self.compute_deadline(); + // Create the accusation let mut accusation = ProofFailureAccusation { e3_id: self.e3_id.clone(), @@ -539,6 +658,7 @@ impl AccusationManager { accused_party_id, proof_type, data_hash, + deadline, signed_payload: forwarded_payload, signature: ArcBytes::default(), }; @@ -557,13 +677,13 @@ impl AccusationManager { return; } - // Cast own vote (agrees: true) + // Cast our own agreement vote (we just observed the failure locally). let mut own_vote = AccusationVote { e3_id: self.e3_id.clone(), accusation_id, voter: self.my_address, - agrees: true, data_hash, + deadline, signature: ArcBytes::default(), }; match self.sign_vote_digest(&own_vote) { @@ -589,7 +709,6 @@ impl AccusationManager { PendingAccusation { accusation, votes_for: vec![own_vote], - votes_against: Vec::new(), timeout_handle: Some(timeout_handle), ec: ec.clone(), }, @@ -659,11 +778,25 @@ impl AccusationManager { return; } - // Determine our vote based on our local verification state + // Determine our position based on our local verification state. + // + // The gossip wire no longer carries disagreement: if our local check + // *passed*, we stay silent (no broadcast, no pending state). The + // accusation will then either reach quorum from other agreeing peers + // or time out as Inconclusive. Only the "we also saw it fail" branch + // and the "we don't have local data yet (C3a/C3b)" branch proceed + // below. let key = (accusation.accused, accusation.proof_type); - let (agrees, our_data_hash) = if let Some(received) = self.received_data.get(&key) { - // We have the data — did our verification also fail? - (!received.verification_passed, received.data_hash) + let our_data_hash = if let Some(received) = self.received_data.get(&key) { + if received.verification_passed { + info!( + "Local verification of {:?} from {} passed — abstaining \ + (no disagreement vote on the wire)", + accusation.proof_type, accusation.accused + ); + return; + } + received.data_hash } else if let Some(ref forwarded) = accusation.signed_payload { // C3a/C3b case: we didn't receive this proof directly. // Validate the forwarded payload's ECDSA, then dispatch async ZK re-verification. @@ -721,7 +854,6 @@ impl AccusationManager { PendingAccusation { accusation, votes_for: Vec::new(), - votes_against: Vec::new(), timeout_handle: Some(timeout_handle), ec: ec.clone(), }, @@ -776,13 +908,15 @@ impl AccusationManager { return; }; - // Cast vote + // We saw the proof fail locally — agree with the accusation. Adopt + // the accuser's deadline so every voter on this accusation signs the + // same on-chain validity window. let mut vote = AccusationVote { e3_id: self.e3_id.clone(), accusation_id, voter: self.my_address, - agrees, data_hash: our_data_hash, + deadline: accusation.deadline, signature: ArcBytes::default(), }; match self.sign_vote_digest(&vote) { @@ -794,10 +928,8 @@ impl AccusationManager { } info!( - "Voting {} on accusation against {} for {:?}", - if agrees { "AGREE" } else { "DISAGREE" }, - accusation.accused, - accusation.proof_type + "Agreeing with accusation against {} for {:?}", + accusation.accused, accusation.proof_type ); // Broadcast vote via gossip @@ -813,12 +945,7 @@ impl AccusationManager { // Record in pending let pending = PendingAccusation { accusation, - votes_for: if agrees { - vec![vote.clone()] - } else { - Vec::new() - }, - votes_against: if agrees { Vec::new() } else { vec![vote] }, + votes_for: vec![vote], timeout_handle: Some(timeout_handle), ec: ec.clone(), }; @@ -882,6 +1009,18 @@ impl AccusationManager { return; }; + // Reject votes whose deadline disagrees with the accusation's chosen + // deadline. All voters must sign the same deadline so the aggregated + // evidence carries a single value for `SlashingManager`'s + // `block.timestamp <= deadline` check. + if vote.deadline != pending.accusation.deadline { + warn!( + "Ignoring vote from {} — deadline {} does not match accusation deadline {}", + vote.voter, vote.deadline, pending.accusation.deadline + ); + return; + } + // Reject votes from the accused party — they have a conflict of interest if vote.voter == pending.accusation.accused { warn!( @@ -892,11 +1031,7 @@ impl AccusationManager { } // Dedup: don't count same voter twice - let already_voted = pending - .votes_for - .iter() - .chain(pending.votes_against.iter()) - .any(|v| v.voter == vote.voter); + let already_voted = pending.votes_for.iter().any(|v| v.voter == vote.voter); if already_voted { return; } @@ -915,22 +1050,26 @@ impl AccusationManager { return; } - if vote.agrees { - pending.votes_for.push(vote); - } else { - pending.votes_against.push(vote); - } + // Every received `AccusationVote` is an agreement (the gossip wire + // carries no disagreement). Append to the agreeing pile and re-check + // quorum. + pending.votes_for.push(vote); - // Check if quorum reached self.check_quorum(vote_accusation_id, ec, ctx); } - /// Evaluate whether we have enough votes to decide. + /// Evaluate whether we have enough agreeing votes to decide. /// /// Quorum logic: - /// - Need >= M agreeing votes → AccusedFaulted - /// - If impossible to reach M even with remaining voters → early exit - /// - data_hash comparison detects equivocation vs false accusation + /// - `>= M` agreeing votes → `AccusedFaulted` (or `Equivocation` if those + /// votes disagree on `data_hash`, indicating the accused sent different + /// bytes to different peers). + /// - Otherwise → keep waiting; the timeout handler decides + /// `Inconclusive` if quorum never arrives. + /// + /// The gossip wire no longer carries disagreement, so there is no + /// fast-fail "quorum unreachable" branch — every silent peer might still + /// agree in flight. Silence beyond `vote_timeout` ⇒ `Inconclusive`. fn check_quorum( &mut self, accusation_id: [u8; 32], @@ -942,77 +1081,32 @@ impl AccusationManager { }; let agree_count = pending.votes_for.len(); - let disagree_count = pending.votes_against.len(); - let total_votes = agree_count + disagree_count; - - // CASE A: Majority says proof is bad → accused is at fault - // But first check for equivocation: if agreeing voters saw different data, - // the accused sent different payloads to different nodes. - if agree_count >= self.threshold_m { - let agree_hashes: HashSet<[u8; 32]> = - pending.votes_for.iter().map(|v| v.data_hash).collect(); - if agree_hashes.len() > 1 { - info!( - "Equivocation detected at quorum: {} unique data hashes among {} agreeing voters for {} {:?}", - agree_hashes.len(), - agree_count, - pending.accusation.accused, - pending.accusation.proof_type - ); - self.emit_quorum_reached(accusation_id, AccusationOutcome::Equivocation, ec, ctx); - } else { - info!( - "Quorum reached: {} votes confirm {} sent bad {:?} proof — AccusedFaulted", - agree_count, pending.accusation.accused, pending.accusation.proof_type - ); - self.emit_quorum_reached(accusation_id, AccusationOutcome::AccusedFaulted, ec, ctx); - } + if agree_count < self.threshold_m { + // Not yet at quorum — wait for more agreement votes or for the + // timeout to fire. return; } - // Check if quorum is still possible. - // Exclude the accused — they cannot vote on their own accusation. - let effective_committee = if self.committee.contains(&pending.accusation.accused) { - self.committee.len().saturating_sub(1) + // Reached `M` — decide between AccusedFaulted and Equivocation by + // checking whether the agreeing voters all saw the same data_hash. + let agree_hashes: HashSet<[u8; 32]> = + pending.votes_for.iter().map(|v| v.data_hash).collect(); + if agree_hashes.len() > 1 { + info!( + "Equivocation detected at quorum: {} unique data hashes among {} agreeing voters for {} {:?}", + agree_hashes.len(), + agree_count, + pending.accusation.accused, + pending.accusation.proof_type + ); + self.emit_quorum_reached(accusation_id, AccusationOutcome::Equivocation, ec, ctx); } else { - self.committee.len() - }; - let remaining = effective_committee.saturating_sub(total_votes); - if agree_count + remaining < self.threshold_m { - // Even if all remaining voters agree, can't reach quorum. - // Collect unique data hashes from actual votes only — do NOT include - // the accusation's data_hash because it is unverified (the accuser's - // own vote already carries their independently-observed hash). - let all_hashes: HashSet<[u8; 32]> = pending - .votes_for - .iter() - .chain(pending.votes_against.iter()) - .map(|v| v.data_hash) - .collect(); - - if all_hashes.len() > 1 { - // Different nodes received different data → equivocation by the accused - info!( - "Equivocation detected: {} unique data hashes for {} {:?}", - all_hashes.len(), - pending.accusation.accused, - pending.accusation.proof_type - ); - self.emit_quorum_reached(accusation_id, AccusationOutcome::Equivocation, ec, ctx); - } else if agree_count <= 1 && disagree_count > 0 { - // Same data, only accuser says bad, others say good → AccuserLied - info!( - "Accuser {} appears to have lied about {} {:?}", - pending.accusation.accuser, - pending.accusation.accused, - pending.accusation.proof_type - ); - self.emit_quorum_reached(accusation_id, AccusationOutcome::AccuserLied, ec, ctx); - } else { - self.emit_quorum_reached(accusation_id, AccusationOutcome::Inconclusive, ec, ctx); - } + info!( + "Quorum reached: {} votes confirm {} sent bad {:?} proof — AccusedFaulted", + agree_count, pending.accusation.accused, pending.accusation.proof_type + ); + self.emit_quorum_reached(accusation_id, AccusationOutcome::AccusedFaulted, ec, ctx); } - // Otherwise: still waiting for more votes — timeout will handle it } /// Called when the vote timeout expires for an accusation. @@ -1021,19 +1115,10 @@ impl AccusationManager { return; // Already resolved }; - // Check for equivocation: if voters saw different data hashes, - // the accused sent different payloads to different nodes. - // Only use actual vote data_hashes — the accusation's data_hash is - // unverified and already represented by the accuser's own vote. - let all_hashes: HashSet<[u8; 32]> = pending - .votes_for - .iter() - .chain(pending.votes_against.iter()) - .map(|v| v.data_hash) - .collect(); - + // All votes received are agreements (the wire carries no + // disagreement signal). At timeout, decide between AccusedFaulted, + // Equivocation, or Inconclusive purely from the agreeing pile. let outcome = if pending.votes_for.len() >= self.threshold_m { - // Check among agreeing voters first let agree_hashes: HashSet<[u8; 32]> = pending.votes_for.iter().map(|v| v.data_hash).collect(); if agree_hashes.len() > 1 { @@ -1041,19 +1126,18 @@ impl AccusationManager { } else { AccusationOutcome::AccusedFaulted } - } else if all_hashes.len() > 1 { - // Not enough votes to convict, but divergent data → equivocation - AccusationOutcome::Equivocation } else { + // Not enough agreements to convict and no signed disagreements + // exist; whether that's silence or active disagreement is + // indistinguishable on the wire. Report Inconclusive. AccusationOutcome::Inconclusive }; warn!( - "Accusation against {} for {:?} timed out with {} for / {} against — outcome: {:?}", + "Accusation against {} for {:?} timed out with {} agreeing votes — outcome: {:?}", pending.accusation.accused, pending.accusation.proof_type, pending.votes_for.len(), - pending.votes_against.len(), outcome ); @@ -1069,7 +1153,6 @@ impl AccusationManager { accused: pending.accusation.accused, proof_type: pending.accusation.proof_type, votes_for: pending.votes_for, - votes_against: pending.votes_against, outcome, evidence, }, @@ -1096,11 +1179,10 @@ impl AccusationManager { } info!( - "Accusation quorum reached for {} {:?}: {} for, {} against — outcome: {}", + "Accusation quorum reached for {} {:?}: {} agreeing votes — outcome: {}", pending.accusation.accused, pending.accusation.proof_type, pending.votes_for.len(), - pending.votes_against.len(), outcome ); @@ -1116,7 +1198,6 @@ impl AccusationManager { accused: pending.accusation.accused, proof_type: pending.accusation.proof_type, votes_for: pending.votes_for, - votes_against: pending.votes_against, outcome, evidence, }, @@ -1143,7 +1224,6 @@ impl AccusationManager { // Purge any votes from the expelled node in pending accusations for pending in self.pending.values_mut() { pending.votes_for.retain(|v| v.voter != data.operator); - pending.votes_against.retain(|v| v.voter != data.operator); } // Purge from buffered votes @@ -1215,9 +1295,7 @@ impl AccusationManager { } }; - let agrees = !zk_passed; // ZK failed → proof is bad → agree with accusation - - // Cache the result for future accusations + // Cache the result for future accusations regardless of outcome. self.cache_verification_result( reverif.accused, reverif.proof_type, @@ -1226,22 +1304,34 @@ impl AccusationManager { reverif.evidence.clone(), ); - // Get ec from the PendingAccusation - let ec = match self.pending.get(&reverif.accusation_id) { - Some(pending) => pending.ec.clone(), + // ZK re-verification passed ⇒ the proof is actually valid ⇒ we + // disagree with the accusation. The gossip wire carries no + // disagreement signal, so just abstain (no broadcast, no pending + // mutation). Other agreeing peers will or won't reach quorum + // independently. + if zk_passed { + info!( + "C3a/C3b re-verification passed for {:?} — abstaining from vote", + reverif.proof_type + ); + return; + } + + // ZK re-verification failed ⇒ we agree with the accusation. + let (ec, deadline) = match self.pending.get(&reverif.accusation_id) { + Some(pending) => (pending.ec.clone(), pending.accusation.deadline), None => { // Accusation already resolved (timeout/quorum) before ZK finished return; } }; - // Cast vote let mut vote = AccusationVote { e3_id: self.e3_id.clone(), accusation_id: reverif.accusation_id, voter: self.my_address, - agrees, data_hash: reverif.data_hash, + deadline, signature: ArcBytes::default(), }; match self.sign_vote_digest(&vote) { @@ -1253,8 +1343,7 @@ impl AccusationManager { } info!( - "C3a/C3b re-verification complete — voting {} on accusation against {:?}", - if agrees { "AGREE" } else { "DISAGREE" }, + "C3a/C3b re-verification confirmed failure for {:?} — agreeing with accusation", reverif.proof_type ); @@ -1265,11 +1354,7 @@ impl AccusationManager { // Record in pending if let Some(pending) = self.pending.get_mut(&reverif.accusation_id) { - if agrees { - pending.votes_for.push(vote); - } else { - pending.votes_against.push(vote); - } + pending.votes_for.push(vote); } // Check quorum @@ -1437,3 +1522,184 @@ impl Handler> for AccusationManager { self.on_consistency_violation(data, &ec, ctx); } } + +// ════════════════════════════════════════════════════════════════════════════ +// Tests +// ════════════════════════════════════════════════════════════════════════════ +// +// These tests pin the actor's EIP-712 digest computation to the exact bytes +// that off-chain test helpers (and ultimately the on-chain +// `SlashingManager._verifyVotes`) expect. If anyone tweaks the typehash +// string, the domain name, or the struct field layout on EITHER side without +// updating the other, these tests fail before the broken signatures ever +// reach the chain. + +#[cfg(test)] +mod tests { + use super::*; + use alloy::primitives::FixedBytes; + use alloy::signers::SignerSync; + + /// Independent re-derivation of the EIP-712 vote digest, mirroring exactly + /// what `SlashingManager._verifyVotes` computes on chain. Kept here (and + /// not imported from a helper) so a regression in the actor's `vote_digest` + /// is caught by a byte-for-byte assertion against a hand-rolled reference. + fn reference_vote_digest( + chain_id: u64, + verifying_contract: Address, + e3_id: u64, + accusation_id: [u8; 32], + voter: Address, + data_hash: [u8; 32], + deadline: u64, + ) -> [u8; 32] { + let domain_typehash: [u8; 32] = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", + ) + .into(); + let name_hash: [u8; 32] = keccak256(VOTE_DOMAIN_NAME).into(); + let version_hash: [u8; 32] = keccak256(VOTE_DOMAIN_VERSION).into(); + let domain_separator: [u8; 32] = keccak256( + &( + domain_typehash, + name_hash, + version_hash, + U256::from(chain_id), + verifying_contract, + ) + .abi_encode(), + ) + .into(); + + let typehash: [u8; 32] = keccak256(VOTE_TYPEHASH_STR).into(); + let struct_hash: [u8; 32] = keccak256( + &( + typehash, + U256::from(e3_id), + FixedBytes::<32>::from(accusation_id), + voter, + FixedBytes::<32>::from(data_hash), + U256::from(deadline), + ) + .abi_encode(), + ) + .into(); + + let mut buf = Vec::with_capacity(2 + 32 + 32); + buf.push(0x19); + buf.push(0x01); + buf.extend_from_slice(&domain_separator); + buf.extend_from_slice(&struct_hash); + keccak256(&buf).into() + } + + /// The actor's `vote_digest` must equal the reference digest byte-for-byte. + /// If this fails, the actor's typehash / domain / struct layout has drifted + /// from what the on-chain verifier expects (or from the constants in + /// `e3_events::accusation_vote`). + #[test] + fn vote_digest_matches_reference() { + let chain_id = 31337u64; + let verifying_contract: Address = "0x9999999999999999999999999999999999999999" + .parse() + .unwrap(); + let voter: Address = "0x2222222222222222222222222222222222222222" + .parse() + .unwrap(); + let accusation_id = [0xab; 32]; + let data_hash = [0xcd; 32]; + let deadline: u64 = 1_700_000_000; + + let vote = AccusationVote { + e3_id: E3id::new("42", chain_id), + accusation_id, + voter, + data_hash, + deadline, + signature: ArcBytes::default(), + }; + + let actor = AccusationManager::vote_digest(&vote, verifying_contract); + let reference = reference_vote_digest( + chain_id, + verifying_contract, + 42, + accusation_id, + voter, + data_hash, + deadline, + ); + + assert_eq!( + actor, reference, + "AccusationManager::vote_digest drifted from the reference EIP-712 \ + computation. Check VOTE_TYPEHASH_STR / VOTE_DOMAIN_NAME against \ + SlashingManager.sol — these MUST stay byte-equal across crates." + ); + } + + /// Sign-and-recover round-trip using the actor's digest. Since + /// `vote_digest_matches_reference` already pins the digest bytes, signing + /// that digest and recovering via `recover_address_from_prehash` must + /// return the voter — i.e. the actor's signatures will be accepted by the + /// on-chain `ECDSA.recover` step. + #[test] + fn actor_signature_recovers_to_voter() { + let signer: PrivateKeySigner = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + .parse() + .unwrap(); + let voter = signer.address(); + let verifying_contract: Address = "0x5555555555555555555555555555555555555555" + .parse() + .unwrap(); + let chain_id = 31337u64; + + let vote = AccusationVote { + e3_id: E3id::new("12345", chain_id), + accusation_id: [0x07; 32], + voter, + data_hash: [0x08; 32], + deadline: 1_700_000_000, + signature: ArcBytes::default(), + }; + + let digest = AccusationManager::vote_digest(&vote, verifying_contract); + let sig = signer + .sign_hash_sync(&FixedBytes::<32>::from(digest)) + .unwrap(); + let recovered = sig + .recover_address_from_prehash(&FixedBytes::<32>::from(digest)) + .expect("recover"); + assert_eq!( + recovered, voter, + "signing the actor's digest and recovering must yield the voter" + ); + } + + /// The accusation digest must include `deadline`. A malicious peer could + /// otherwise rewrite the deadline in transit without invalidating the + /// accuser's signature. Guard: changing only `deadline` must change the + /// digest. + #[test] + fn accusation_digest_binds_deadline() { + let make = |deadline: u64| ProofFailureAccusation { + e3_id: E3id::new("9", 31337), + accuser: "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + .parse() + .unwrap(), + accused: "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + .parse() + .unwrap(), + accused_party_id: 1, + proof_type: ProofType::C1PkGeneration, + data_hash: [0x42; 32], + deadline, + signed_payload: None, + signature: ArcBytes::default(), + }; + let a = AccusationManager::accusation_digest(&make(1_700_000_000)); + let b = AccusationManager::accusation_digest(&make(1_700_000_001)); + assert_ne!(a, b, "deadline must be part of the accusation digest"); + } +} diff --git a/crates/zk-prover/src/actors/accusation_manager_ext.rs b/crates/zk-prover/src/actors/accusation_manager_ext.rs index 85909b6b4..a41691c0c 100644 --- a/crates/zk-prover/src/actors/accusation_manager_ext.rs +++ b/crates/zk-prover/src/actors/accusation_manager_ext.rs @@ -24,6 +24,11 @@ pub struct AccusationManagerExtension { signer: PrivateKeySigner, /// On-chain `SlashingManager` address (EIP-712 `verifyingContract` for vote sigs). slashing_manager: Address, + /// Registry-wide off-chain freshness window (seconds), read from + /// `CiphernodeRegistry.accusationVoteValidity()` at process startup. + /// Passed to every per-E3 actor; governance changes require a node restart + /// to take effect (same lifecycle contract as `slashing_manager`). + vote_validity_secs: u64, } impl AccusationManagerExtension { @@ -31,11 +36,13 @@ impl AccusationManagerExtension { bus: &BusHandle, signer: PrivateKeySigner, slashing_manager: Address, + vote_validity_secs: u64, ) -> Box { Box::new(Self { bus: bus.clone(), signer: signer.clone(), slashing_manager, + vote_validity_secs, }) } } @@ -97,6 +104,7 @@ impl E3Extension for AccusationManagerExtension { self.slashing_manager, committee_addresses, threshold_m, + self.vote_validity_secs, meta.params_preset, ); diff --git a/crates/zk-prover/src/actors/mod.rs b/crates/zk-prover/src/actors/mod.rs index 026eccc9a..d3525f69f 100644 --- a/crates/zk-prover/src/actors/mod.rs +++ b/crates/zk-prover/src/actors/mod.rs @@ -66,8 +66,8 @@ use crate::ZkBackend; /// /// Requires a `ZkBackend` for proof generation/verification and a /// `PrivateKeySigner` for signing proofs (fault attribution). -/// `dkg_fold_attestation_verifier` is the on-chain verifier address used as -/// the EIP-712 `verifyingContract` for fold attestations; required when +/// `dkg_fold_attestation_verifier` is `CiphernodeRegistry.dkgFoldAttestationVerifier()` +/// (EIP-712 `verifyingContract` for fold attestations). Fetched at node startup when /// proof aggregation is enabled. pub fn setup_zk_actors( bus: &BusHandle, diff --git a/crates/zk-prover/src/actors/node_proof_aggregator.rs b/crates/zk-prover/src/actors/node_proof_aggregator.rs index 971db6431..a06100128 100644 --- a/crates/zk-prover/src/actors/node_proof_aggregator.rs +++ b/crates/zk-prover/src/actors/node_proof_aggregator.rs @@ -366,7 +366,7 @@ impl NodeProofAggregator { error!( e3_id = %e3_id, party_id, - "NodeProofAggregator: cannot sign DkgFoldAttestation — `dkg_fold_attestation_verifier` address not configured" + "NodeProofAggregator: cannot sign DkgFoldAttestation — CiphernodeRegistry.dkgFoldAttestationVerifier not configured" ); None } diff --git a/crates/zk-prover/src/actors/proof_request.rs b/crates/zk-prover/src/actors/proof_request.rs index c0611ed0c..d342a29a5 100644 --- a/crates/zk-prover/src/actors/proof_request.rs +++ b/crates/zk-prover/src/actors/proof_request.rs @@ -20,9 +20,9 @@ use e3_events::{ EventContext, EventPublisher, EventSubscriber, EventType, FailureReason, PkAggregationProofPending, PkAggregationProofRequest, PkAggregationProofSigned, PkBfvProofRequest, PkGenerationProofSigned, Proof, ProofPayload, ProofType, - ProofVerificationPassed, Sequenced, ShareDecryptionProofPending, SignedProofFailed, - SignedProofPayload, ThresholdShare, ThresholdShareCreated, ThresholdSharePending, TypedEvent, - ZkRequest, ZkResponse, + ProofVerificationPassed, Sequenced, ShareDecryptionProofPending, SignedProofPayload, + ThresholdShare, ThresholdShareCreated, ThresholdSharePending, TypedEvent, ZkRequest, + ZkResponse, }; use e3_utils::utility_types::ArcBytes; use e3_utils::NotifySync; diff --git a/crates/zk-prover/src/actors/proof_verification.rs b/crates/zk-prover/src/actors/proof_verification.rs index 3b8e71ef4..93651d379 100644 --- a/crates/zk-prover/src/actors/proof_verification.rs +++ b/crates/zk-prover/src/actors/proof_verification.rs @@ -15,10 +15,10 @@ use actix::{Actor, Addr, AsyncContext, Context, Handler, Message, Recipient}; use alloy::primitives::{keccak256, Address, Bytes}; use alloy::sol_types::SolValue; use e3_events::{ - BusHandle, CiphernodeSelected, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, - EncryptionKeyCreated, EncryptionKeyReceived, EventContext, EventPublisher, EventSubscriber, - EventType, Proof, ProofType, ProofVerificationFailed, ProofVerificationPassed, Sequenced, - SignedProofFailed, SignedProofPayload, TypedEvent, + BusHandle, E3id, EnclaveEvent, EnclaveEventData, EncryptionKey, EncryptionKeyCreated, + EncryptionKeyReceived, EventContext, EventPublisher, EventSubscriber, EventType, Proof, + ProofType, ProofVerificationFailed, ProofVerificationPassed, Sequenced, SignedProofFailed, + SignedProofPayload, TypedEvent, }; use e3_fhe_params::BfvPreset; use e3_utils::NotifySync; diff --git a/crates/zk-prover/src/prover.rs b/crates/zk-prover/src/prover.rs index 262e8fe02..49fa0cead 100644 --- a/crates/zk-prover/src/prover.rs +++ b/crates/zk-prover/src/prover.rs @@ -11,8 +11,18 @@ use e3_utils::utility_types::ArcBytes; use std::fs; use std::path::PathBuf; use std::process::Command as StdCommand; +use std::sync::atomic::{AtomicU64, Ordering}; use tracing::{debug, info, warn}; +/// Unique bb job directories — shared [`ZkBackend::work_dir`] must not reuse the same paths +/// when prove/verify runs concurrently (integration harness + `multithread_concurrent_jobs` > 1). +static BB_WORK_JOB_COUNTER: AtomicU64 = AtomicU64::new(0); + +fn next_bb_work_subdir(prefix: &str) -> String { + let id = BB_WORK_JOB_COUNTER.fetch_add(1, Ordering::Relaxed); + format!("{prefix}_{id}") +} + pub struct ZkProver { bb_binary: PathBuf, circuits_dir: PathBuf, @@ -160,7 +170,10 @@ impl ZkProver { ))); } - let job_dir = self.work_dir.join(e3_id); + let job_dir = self + .work_dir + .join(e3_id) + .join(next_bb_work_subdir(&format!("prove_{}", circuit.as_str()))); let witness_path = job_dir.join("witness.gz"); let output_dir = job_dir.join("out"); fs::create_dir_all(&job_dir)?; @@ -323,8 +336,6 @@ impl ZkProver { ))); } - let verification_subdir = format!("verify_party_{}", party_id); - debug!( "verifying proof for circuit {} (party {}) using VK: {}", circuit.as_str(), @@ -332,7 +343,10 @@ impl ZkProver { vk_path.display() ); - let job_dir = self.work_dir.join(e3_id).join(&verification_subdir); + let job_dir = self.work_dir.join(e3_id).join(next_bb_work_subdir(&format!( + "verify_party_{party_id}_{}", + circuit.as_str() + ))); let out_dir = job_dir.join("out"); fs::create_dir_all(&out_dir)?; diff --git a/crates/zk-prover/tests/slashing_integration_tests.rs b/crates/zk-prover/tests/slashing_integration_tests.rs index 1e2157541..a47077df2 100644 --- a/crates/zk-prover/tests/slashing_integration_tests.rs +++ b/crates/zk-prover/tests/slashing_integration_tests.rs @@ -369,15 +369,19 @@ fn test_digest_matches_solidity_encoding() { // ════════════════════════════════════════════════════════════════════════════ // Attestation vote helpers — used by both pure Rust and on-chain tests +// +// The vote typehash / domain name / domain version are imported from +// `e3_events` so the test helper and the production `AccusationManager` actor +// always hash the SAME bytes. Adding a fourth source of truth here would +// reintroduce exactly the drift class this test layout exists to prevent. // ════════════════════════════════════════════════════════════════════════════ -const VOTE_TYPEHASH_STR: &str = - "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)"; +use e3_events::{VOTE_DOMAIN_NAME, VOTE_DOMAIN_VERSION, VOTE_TYPEHASH_STR}; const VOTE_DOMAIN_TYPEHASH_STR: &str = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; -const VOTE_DOMAIN_NAME: &str = "EnclaveSlashingManager"; -const VOTE_DOMAIN_VERSION: &str = "1"; +/// Sentinel deadline matching Hardhat `ethers.MaxUint256` (no expiry in tests). +const VOTE_NO_EXPIRY: U256 = U256::MAX; /// Lane A policy key: `keccak256(abi.encodePacked(proofType))` (must match `SlashingManager.proposeSlash`). fn reason_for_proof_type(proof_type: u8) -> FixedBytes<32> { @@ -439,8 +443,8 @@ fn compute_vote_digest( e3_id: u64, accusation_id: FixedBytes<32>, voter: Address, - agrees: bool, data_hash: FixedBytes<32>, + deadline: U256, ) -> FixedBytes<32> { let typehash = keccak256(VOTE_TYPEHASH_STR); let struct_hash = keccak256( @@ -449,8 +453,8 @@ fn compute_vote_digest( U256::from(e3_id), accusation_id, voter, - agrees, data_hash, + deadline, ) .abi_encode(), ); @@ -470,8 +474,27 @@ fn sign_vote( verifying_contract: Address, e3_id: u64, accusation_id: FixedBytes<32>, - agrees: bool, data_hash: FixedBytes<32>, +) -> (Address, Bytes) { + sign_vote_with_deadline( + signer, + chain_id, + verifying_contract, + e3_id, + accusation_id, + data_hash, + VOTE_NO_EXPIRY, + ) +} + +fn sign_vote_with_deadline( + signer: &PrivateKeySigner, + chain_id: u64, + verifying_contract: Address, + e3_id: u64, + accusation_id: FixedBytes<32>, + data_hash: FixedBytes<32>, + deadline: U256, ) -> (Address, Bytes) { let voter = signer.address(); let digest = compute_vote_digest( @@ -480,8 +503,8 @@ fn sign_vote( e3_id, accusation_id, voter, - agrees, data_hash, + deadline, ); // EIP-712: sign the typed-data hash directly (no EIP-191 wrapping). let sig = signer @@ -492,31 +515,22 @@ fn sign_vote( /// Encode attestation evidence for `proposeSlash()`. /// -/// Format: `abi.encode(uint256 proofType, address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures, bytes evidence)` -/// Voters are sorted ascending by address (contract requires strict ascending order). -/// `evidence` is the preimage of `dataHash` (the contract enforces `keccak256(evidence) == commonDataHash`). +/// Format: `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, +/// uint256 deadline, bytes[] signatures)`. Voters are sorted ascending by address. fn encode_attestation_evidence( proof_type: u8, - mut votes: Vec<(Address, bool, FixedBytes<32>, Bytes)>, - evidence: Bytes, + mut votes: Vec<(Address, FixedBytes<32>, Bytes)>, + deadline: U256, ) -> Bytes { - votes.sort_by_key(|(addr, _, _, _)| *addr); + votes.sort_by_key(|(addr, _, _)| *addr); - let voters: Vec
= votes.iter().map(|(a, _, _, _)| *a).collect(); - let agrees: Vec = votes.iter().map(|(_, a, _, _)| *a).collect(); - let data_hashes: Vec> = votes.iter().map(|(_, _, d, _)| *d).collect(); - let sigs: Vec = votes.iter().map(|(_, _, _, s)| s.clone()).collect(); + let voters: Vec
= votes.iter().map(|(a, _, _)| *a).collect(); + let data_hashes: Vec> = votes.iter().map(|(_, d, _)| *d).collect(); + let sigs: Vec = votes.iter().map(|(_, _, s)| s.clone()).collect(); // `abi_encode_params` matches Solidity `abi.encode(a,b,...)`; `abi_encode` adds an extra // outer offset word that breaks `abi.decode(proof, (uint256))` in `proposeSlash`. - ( - U256::from(proof_type), - voters, - agrees, - data_hashes.to_vec(), - sigs, - evidence, - ) + (U256::from(proof_type), voters, data_hashes, deadline, sigs) .abi_encode_params() .into() } @@ -540,7 +554,7 @@ fn test_reason_for_proof_type_matches_solidity() { fn test_vote_typehash() { let expected: [u8; 32] = keccak256(VOTE_TYPEHASH_STR).into(); // Cross-check with the exact string the Solidity contract uses: - let sol_str = "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bool agrees,bytes32 dataHash)"; + let sol_str = "AccusationVote(uint256 e3Id,bytes32 accusationId,address voter,bytes32 dataHash,uint256 deadline)"; let sol_hash: [u8; 32] = keccak256(sol_str).into(); assert_eq!( expected, sol_hash, @@ -572,8 +586,8 @@ fn test_vote_digest_manual_computation() { e3_id, accusation_id, voter, - true, data_hash, + VOTE_NO_EXPIRY, ); // Manual EIP-712 computation @@ -584,8 +598,8 @@ fn test_vote_digest_manual_computation() { U256::from(e3_id), accusation_id, voter, - true, data_hash, + VOTE_NO_EXPIRY, ) .abi_encode(), ); @@ -628,7 +642,6 @@ fn test_vote_signing_roundtrip() { verifying_contract, e3_id, accusation_id, - true, data_hash, ); @@ -645,8 +658,8 @@ fn test_vote_signing_roundtrip() { e3_id, accusation_id, voter, - true, data_hash, + VOTE_NO_EXPIRY, ); let sig = alloy::primitives::Signature::try_from(sig_bytes.as_ref()).expect("signature should parse"); @@ -670,7 +683,6 @@ fn test_evidence_leading_word_is_proof_type() { "0x1111111111111111111111111111111111111111" .parse() .unwrap(), - true, FixedBytes::from([1u8; 32]), Bytes::from(vec![0u8; 65]), ), @@ -678,12 +690,11 @@ fn test_evidence_leading_word_is_proof_type() { "0x2222222222222222222222222222222222222222" .parse() .unwrap(), - true, FixedBytes::from([2u8; 32]), Bytes::from(vec![0u8; 65]), ), ], - Bytes::new(), + VOTE_NO_EXPIRY, ); let leading = U256::from_be_slice(&evidence[..32]); assert_eq!(leading, U256::ZERO, "leading word must be proofType"); @@ -709,16 +720,13 @@ fn test_attestation_evidence_encoding() { let accusation_id = compute_accusation_id(chain_id, e3_id, operator, proof_type); - // Evidence preimage must hash to dataHash on chain. - let evidence_bytes = Bytes::from(vec![0xab, 0xcd, 0xef]); - let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); + let data_hash = FixedBytes::from([0xab; 32]); let (voter1, sig1) = sign_vote( &signer1, chain_id, verifying_contract, e3_id, accusation_id, - true, data_hash, ); let (voter2, sig2) = sign_vote( @@ -727,46 +735,33 @@ fn test_attestation_evidence_encoding() { verifying_contract, e3_id, accusation_id, - true, data_hash, ); let evidence = encode_attestation_evidence( proof_type, - vec![ - (voter1, true, data_hash, sig1), - (voter2, true, data_hash, sig2), - ], - evidence_bytes.clone(), + vec![(voter1, data_hash, sig1), (voter2, data_hash, sig2)], + VOTE_NO_EXPIRY, ); - // Decode and verify structure: (uint256, address[], bool[], bytes32[], bytes[], bytes) - type AttestationTuple = ( - U256, - Vec
, - Vec, - Vec>, - Vec, - Bytes, - ); + // Decode and verify structure: (uint256, address[], bytes32[], uint256, bytes[]) + type AttestationTuple = (U256, Vec
, Vec>, U256, Vec); let decoded = AttestationTuple::abi_decode_params(&evidence).expect("evidence should ABI-decode"); - let (dec_proof_type, dec_voters, dec_agrees, dec_hashes, dec_sigs, dec_evidence) = decoded; + let (dec_proof_type, dec_voters, dec_hashes, dec_deadline, dec_sigs) = decoded; assert_eq!(dec_proof_type, U256::from(proof_type), "proofType mismatch"); assert_eq!(dec_voters.len(), 2, "should have 2 voters"); assert!( dec_voters[0] < dec_voters[1], "voters should be sorted ascending" ); - assert!(dec_agrees.iter().all(|a| *a), "all votes should agree"); assert_eq!(dec_hashes.len(), 2, "should have 2 data hashes"); + assert_eq!(dec_deadline, VOTE_NO_EXPIRY, "deadline mismatch"); assert_eq!(dec_sigs.len(), 2, "should have 2 signatures"); - assert_eq!(dec_evidence, evidence_bytes, "evidence bytes mismatch"); - let recomputed: FixedBytes<32> = keccak256(&dec_evidence).into(); - assert_eq!( - recomputed, dec_hashes[0], - "keccak256(evidence) must equal commonDataHash" + assert!( + dec_hashes.iter().all(|h| *h == data_hash), + "all voters must share the same dataHash" ); } @@ -789,8 +784,8 @@ async fn deploy_and_configure( // Deploy returner for bondingRegistry (slashTicketBalance returns uint256) let returner_addr = deploy_contract(provider, RETURNER_DEPLOY_BYTECODE, &[]).await; - // Deploy SlashingManager(admin) — constructor only takes admin address - let sm_args = admin.abi_encode(); + // Deploy SlashingManager(initialDelay, admin) — use 0 delay for local tests + let sm_args = (0u64, admin).abi_encode(); let sm_addr = deploy_contract(provider, sm_bytecode, &sm_args).await; // Configure dependencies via admin functions @@ -931,10 +926,9 @@ async fn test_onchain_valid_attestation_executes_slash() { .await .unwrap(); - // All 3 voters sign accusation votes (agrees=true) + // All 3 voters sign accusation votes let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let evidence_bytes = Bytes::from(vec![0xaa]); - let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); + let data_hash = FixedBytes::from([0xaa; 32]); let (v1, s1) = sign_vote( &voter_signer1, @@ -942,7 +936,6 @@ async fn test_onchain_valid_attestation_executes_slash() { sm_addr, e3_id, accusation_id, - true, data_hash, ); let (v2, s2) = sign_vote( @@ -951,7 +944,6 @@ async fn test_onchain_valid_attestation_executes_slash() { sm_addr, e3_id, accusation_id, - true, data_hash, ); let (v3, s3) = sign_vote( @@ -960,18 +952,17 @@ async fn test_onchain_valid_attestation_executes_slash() { sm_addr, e3_id, accusation_id, - true, data_hash, ); let evidence = encode_attestation_evidence( proof_type, vec![ - (v1, true, data_hash, s1), - (v2, true, data_hash, s2), - (v3, true, data_hash, s3), + (v1, data_hash, s1), + (v2, data_hash, s2), + (v3, data_hash, s3), ], - evidence_bytes, + VOTE_NO_EXPIRY, ); // Verify proposal count before @@ -1113,12 +1104,11 @@ async fn test_onchain_insufficient_attestations_reverts() { sm_addr, e3_id, accusation_id, - true, data_hash, ); let evidence = - encode_attestation_evidence(proof_type, vec![(v1, true, data_hash, s1)], Bytes::new()); + encode_attestation_evidence(proof_type, vec![(v1, data_hash, s1)], VOTE_NO_EXPIRY); let result = slashing_mgr .proposeSlash(U256::from(e3_id), operator_addr, evidence) @@ -1220,8 +1210,7 @@ async fn test_onchain_voter_not_in_committee_reverts() { // Outsider signs a vote (valid signature, but not a committee member) let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let evidence_bytes = Bytes::from(vec![0xcc]); - let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); + let data_hash = FixedBytes::from([0xcc; 32]); let (v_out, s_out) = sign_vote( &outsider_signer, @@ -1229,15 +1218,11 @@ async fn test_onchain_voter_not_in_committee_reverts() { sm_addr, e3_id, accusation_id, - true, data_hash, ); - let evidence = encode_attestation_evidence( - proof_type, - vec![(v_out, true, data_hash, s_out)], - evidence_bytes, - ); + let evidence = + encode_attestation_evidence(proof_type, vec![(v_out, data_hash, s_out)], VOTE_NO_EXPIRY); let result = slashing_mgr .proposeSlash(U256::from(e3_id), operator_addr, evidence) @@ -1339,8 +1324,7 @@ async fn test_onchain_invalid_vote_signature_reverts() { // Impersonator signs the vote with their key, but we claim it's from victim_signer let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let evidence_bytes = Bytes::from(vec![0xdd]); - let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); + let data_hash = FixedBytes::from([0xdd; 32]); // Sign using impersonator's key but construct the digest for victim_signer's address let digest = compute_vote_digest( @@ -1349,8 +1333,8 @@ async fn test_onchain_invalid_vote_signature_reverts() { e3_id, accusation_id, victim_signer.address(), - true, data_hash, + VOTE_NO_EXPIRY, ); let bad_sig = impersonator_signer .sign_hash_sync(&digest) @@ -1360,10 +1344,9 @@ async fn test_onchain_invalid_vote_signature_reverts() { let evidence: Bytes = ( U256::from(proof_type), vec![victim_signer.address()], - vec![true], vec![data_hash], + VOTE_NO_EXPIRY, vec![Bytes::from(bad_sig.as_bytes().to_vec())], - evidence_bytes, ) .abi_encode_params() .into(); @@ -1469,8 +1452,7 @@ async fn test_onchain_duplicate_voter_reverts() { // Create TWO votes from the same voter (duplicate addresses) let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let evidence_bytes = Bytes::from(vec![0xee]); - let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); + let data_hash = FixedBytes::from([0xee; 32]); let (voter, sig) = sign_vote( &voter_signer, @@ -1478,7 +1460,6 @@ async fn test_onchain_duplicate_voter_reverts() { sm_addr, e3_id, accusation_id, - true, data_hash, ); @@ -1487,10 +1468,9 @@ async fn test_onchain_duplicate_voter_reverts() { let evidence: Bytes = ( U256::from(proof_type), vec![voter, voter], - vec![true, true], vec![data_hash, data_hash], + VOTE_NO_EXPIRY, vec![sig.clone(), sig], - evidence_bytes, ) .abi_encode_params() .into(); @@ -1597,8 +1577,7 @@ async fn test_onchain_duplicate_evidence_reverts() { .unwrap(); let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); - let evidence_bytes = Bytes::from(vec![0xff]); - let data_hash: FixedBytes<32> = keccak256(&evidence_bytes).into(); + let data_hash = FixedBytes::from([0xff; 32]); let (v1, s1) = sign_vote( &voter_signer1, @@ -1606,7 +1585,6 @@ async fn test_onchain_duplicate_evidence_reverts() { sm_addr, e3_id, accusation_id, - true, data_hash, ); let (v2, s2) = sign_vote( @@ -1615,14 +1593,13 @@ async fn test_onchain_duplicate_evidence_reverts() { sm_addr, e3_id, accusation_id, - true, data_hash, ); let evidence = encode_attestation_evidence( proof_type, - vec![(v1, true, data_hash, s1), (v2, true, data_hash, s2)], - evidence_bytes, + vec![(v1, data_hash, s1), (v2, data_hash, s2)], + VOTE_NO_EXPIRY, ); // First submission should succeed @@ -1654,3 +1631,180 @@ async fn test_onchain_duplicate_evidence_reverts() { println!("PASS: duplicate evidence correctly reverts — replay protection verified"); } + +// ════════════════════════════════════════════════════════════════════════════ +// End-to-end actor parity (Anvil) +// +// Drives the production `AccusationManager::vote_digest` and +// `e3_evm::encode_attestation_evidence` against a deployed `SlashingManager` +// on Anvil. Catches drift between off-chain signing/encoding and the +// on-chain decoder/recover that hand-rolled reference helpers cannot — if +// any of (typehash string, domain literal, field order, deadline binding) +// silently diverges, this test reverts on-chain. +// ════════════════════════════════════════════════════════════════════════════ + +/// The actor's `AccusationManager::vote_digest` + `e3_evm::encode_attestation_evidence` +/// must produce calldata that `SlashingManager._verifyAttestationEvidence` +/// accepts. This is the canonical "actor → Solidity" end-to-end test. +#[tokio::test] +async fn test_onchain_actor_signed_vote_accepted() { + use e3_events::{AccusationOutcome, AccusationQuorumReached, AccusationVote, ProofType}; + use e3_evm::encode_attestation_evidence; + use e3_zk_prover::AccusationManager; + + if !find_anvil().await { + println!("skipping: anvil not found on PATH"); + return; + } + + let (sm_bytecode, mr_bytecode) = match load_slashing_artifacts() { + Some(artifacts) => artifacts, + None => { + println!( + "skipping: contract artifacts not found \ + (run `npx hardhat compile` in packages/enclave-contracts)" + ); + return; + } + }; + + let provider = ProviderBuilder::new().connect_anvil_with_wallet(); + let chain_id = provider.get_chain_id().await.unwrap(); + + let voter1 = PrivateKeySigner::random(); + let voter2 = PrivateKeySigner::random(); + let voter3 = PrivateKeySigner::random(); + let operator_addr: Address = "0x4444444444444444444444444444444444444444" + .parse() + .unwrap(); + + let mock_registry_addr = deploy_contract(&provider, &mr_bytecode, &[]).await; + let mock_registry = MockCiphernodeRegistry::new(mock_registry_addr, &provider); + let (sm_addr, _admin) = deploy_and_configure(&provider, &sm_bytecode, mock_registry_addr).await; + let slashing_mgr = SlashingManager::new(sm_addr, &provider); + + let e3_id: u64 = 7; + let proof_type = 0u8; + let reason = reason_for_proof_type(proof_type); + + // Enable an attestation-based policy. `appealWindow = 0` keeps the + // assertion focused on the verifier path (the slash auto-executes). + slashing_mgr + .setSlashPolicy( + reason, + SlashingManager::SlashPolicy { + ticketPenalty: U256::from(50_000_000u64), + licensePenalty: U256::from(100_000_000_000_000_000_000u128), + requiresProof: true, + proofVerifier: Address::ZERO, + banNode: false, + appealWindow: U256::ZERO, + enabled: true, + affectsCommittee: false, + failureReason: 0u8, + }, + ) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + // Committee = operator + 3 voters; threshold M=2. + let committee = vec![ + operator_addr, + voter1.address(), + voter2.address(), + voter3.address(), + ]; + mock_registry + .setCommitteeNodes(U256::from(e3_id), committee) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + mock_registry + .setThreshold(U256::from(e3_id), 2u32) + .send() + .await + .unwrap() + .get_receipt() + .await + .unwrap(); + + let data_hash = FixedBytes::from([0xee; 32]); + + // Pick a deadline far in the future so the on-chain check passes + // regardless of Anvil's block.timestamp at submission time. + let deadline: u64 = u64::MAX / 2; + + let accusation_id = compute_accusation_id(chain_id, e3_id, operator_addr, proof_type); + + // Build & sign three votes via the **production** code path: + // 1. Construct `AccusationVote` exactly as the actor would. + // 2. Compute the digest via the actor's `vote_digest`. + // 3. Sign with `signer.sign_hash_sync` (same as `sign_vote_digest`). + let make_actor_vote = |signer: &PrivateKeySigner| -> AccusationVote { + let voter = signer.address(); + let mut vote = AccusationVote { + e3_id: e3_events::E3id::new(e3_id.to_string(), chain_id), + accusation_id: *accusation_id.as_ref(), + voter, + data_hash: *data_hash.as_ref(), + deadline, + signature: ArcBytes::default(), + }; + let digest = AccusationManager::vote_digest(&vote, sm_addr); + let sig = signer + .sign_hash_sync(&FixedBytes::<32>::from(digest)) + .expect("vote sign"); + vote.signature = ArcBytes::from_bytes(&sig.as_bytes()); + vote + }; + + let votes_for = vec![ + make_actor_vote(&voter1), + make_actor_vote(&voter2), + make_actor_vote(&voter3), + ]; + + // Build the event the production writer consumes and encode via the + // **production** encoder. If either side has drifted from Solidity, + // `proposeSlash` will revert (InvalidVoteSignature, EquivocationDetected, + // or ABI-decode failure). + let quorum = AccusationQuorumReached { + e3_id: e3_events::E3id::new(e3_id.to_string(), chain_id), + accuser: voter1.address(), + accused: operator_addr, + proof_type: ProofType::C0PkBfv, + votes_for, + outcome: AccusationOutcome::AccusedFaulted, + evidence: Bytes::new(), // audit metadata only; not on chain + }; + let evidence = encode_attestation_evidence(&quorum) + .expect("encode_attestation_evidence must produce bytes for nonempty votes_for"); + + // Submit. If anything in the actor → writer → Solidity chain disagrees, + // this call reverts. The decoded Solidity error is far more informative + // than a digest-mismatch assertion, which is the whole point of running + // against the real contract. + let receipt = slashing_mgr + .proposeSlash(U256::from(e3_id), operator_addr, Bytes::from(evidence)) + .send() + .await + .expect("actor-signed proposeSlash must succeed (off-chain ↔ on-chain digest parity)") + .get_receipt() + .await + .expect("proposeSlash receipt obtainable"); + assert!( + receipt.status(), + "actor-signed proposeSlash must land on chain — receipt status was false" + ); + println!( + "PASS: actor-signed AccusationVote accepted by Solidity verifier (tx={:?})", + receipt.transaction_hash + ); +} diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index 6abc4db7b..bf7be16e7 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -410,6 +410,49 @@ read to identify which registered operator produced which DKG output. The mappin (via committee hash), the attestation binds to the proof commitments and the operator key, and the operator key was registered against `topNodes` at committee finalisation. +### Fold-attestation verifier rotation (operator restart requirement) + +The address of the on-chain `DkgFoldAttestationVerifier` is governance-mutable on the registry via a +2-day timelocked propose / commit / cancel flow (`DKG_FOLD_VERIFIER_TIMELOCK`). This is intentional +— the verifier is treated as a critical admin key so a compromised owner cannot instantly swap in a +weakened verifier that bypasses per-party attestation checks. + +**Node-operator requirement.** Ciphernodes fetch `dkgFoldAttestationVerifier()` from the registry +**once at process startup** and use the returned address as the EIP-712 `verifyingContract` for +every fold attestation they sign during the process lifetime (see +[`fetch_dkg_fold_attestation_verifier`](https://github.com/gnosisguild/enclave/blob/main/crates/evm/src/ciphernode_registry_sol.rs) +and the `setup_zk_actors` call in `ciphernode-builder`). After a successful +`commitDkgFoldAttestationVerifier`, signatures produced by long-running nodes will be rejected +on-chain by the new verifier — different `verifyingContract` means a different EIP-712 domain +separator, so `ECDSA.recover` returns the wrong address, the canonical-slot binding check fails, and +aggregators will treat those nodes as dishonest until they restart. + +The 2-day timelock window exists for exactly this reason: it gives operators time to coordinate a +rolling restart before the swap becomes effective. Operators MUST restart all ciphernodes within +`DKG_FOLD_VERIFIER_TIMELOCK` of the propose event being emitted; this is a strict requirement, not a +recommendation. Nodes that miss the window silently produce invalid attestations until restarted. +The same rule applies to the one-shot deploy-time `setInitialDkgFoldAttestationVerifier`, except +that call is only valid when no verifier is yet configured. + +**Multi-chain caveat.** When `enclave.config.yaml` lists more than one chain, the node reads +`dkgFoldAttestationVerifier` and `accusationVoteValidity` from the **first enabled** chain only +(`fetch_dkg_fold_attestation_verifier_from_registry` / +`fetch_accusation_vote_validity_from_registry` in `ciphernode-builder`). That single value is then +used for every E3 the node participates in, regardless of which chain the E3 lives on. This is a +deliberate trade-off: + +- **Today most operators run single-chain nodes**, so the practical risk is zero. +- A multi-chain node whose first-enabled chain has a _stricter_ setter for either knob produces + values the other chain's contract may refuse — e.g. a `verifyingContract` on chain A is + meaningless when chain B's verifier recomputes the EIP-712 domain against its own address. There + is no in-flight fallback. +- If you add a new chain to a running node's config, or rotate any chain's verifier / vote-validity + window, restart the node so all per-chain fetches re-run. + +A robust per-E3 lookup (re-fetch from the chain that owns the E3 every time we sign or encode) is +left as future work — the design is straightforward but the wiring crosses multiple actors and isn't +worth the surface area until a multi-chain deployment makes the trade-off concrete. + ### Keeping the on-chain Honk verifiers in sync The two on-chain Honk verifiers consumed by `publishCommittee` and the decryption flow diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index 3b681cc95..ed52e89d5 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -22,9 +22,6 @@ chains: fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" deploy_block: 4 - dkg_fold_attestation_verifier: - address: "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0" - deploy_block: 40 - name: "sepolia" enabled: false # Public Sepolia WebSocket endpoint (see repo docs for the recommended default). diff --git a/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts b/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts index 29c215a97..893a7822e 100644 --- a/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts +++ b/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts @@ -18,7 +18,6 @@ const contractMapping: Record = { BondingRegistry: 'bonding_registry', SlashingManager: 'slashing_manager', MockUSDC: 'fee_token', - DkgFoldAttestationVerifier: 'dkg_fold_attestation_verifier', } // Get __dirname equivalent in ES modules diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 760d163ff..b54830663 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -3159,5 +3159,5 @@ }, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" + "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 7b1f01d6c..894fc9482 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -1062,5 +1062,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" + "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index a10a49010..9418eac6c 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -249,6 +249,19 @@ "name": "ZeroAddress", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + } + ], + "name": "AccusationVoteValiditySet", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -1186,5 +1199,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" + "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index e7d6b70c7..2537b9f11 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2427,5 +2427,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" + "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index f61c834dd..a3b619af7 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -1186,5 +1186,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" + "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index d3c0362c4..e1f4ae4f1 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -307,6 +307,19 @@ "name": "ZeroAddress", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + } + ], + "name": "AccusationVoteValiditySet", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -766,6 +779,19 @@ "name": "TicketSubmitted", "type": "event" }, + { + "inputs": [], + "name": "DEFAULT_ACCUSATION_VOTE_VALIDITY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "DKG_FOLD_VERIFIER_TIMELOCK", @@ -838,6 +864,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "accusationVoteValidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1572,6 +1611,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "setAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1727,30 +1779,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b614a40806100d96000396000f3fe608060405234801561001057600080fd5b50600436106102935760003560e01c806301ffc9a714610298578063096b810a146102c0578063099a161a146102d55780630b45f8e9146102f65780630bbfade7146103095780630f3e34121461031c57806317d611201461032f5780631e08d0e8146103505780632800d82914610358578063291a691b1461036b5780632e7b716d1461037e578063323beaa5146103915780634d6861a6146103a457806350e6d94c146103b757806350e6fad3146103da5780635d504776146103e457806362cc89a8146103f757806370e36bbe14610417578063715018a61461042a57806379ba5097146104325780637c92f5241461043a57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ad5780638da5cb5b146104c35780638e5ce3ad146104cb5780639015d371146104de5780639a7a2ffc146104f15780639f0f874a1461052e578063a016493014610537578063a8a4d69b14610557578063acc524941461056a578063b2d5d1ac1461057d578063b8ab470414610586578063bbe4b803146105a8578063bff232c1146105b2578063c2b40ae4146105c5578063c3a0ec30146105e5578063c8fe182d146105f6578063ca2869a0146105fe578063cd6dc6871461061e578063cf90b6ed14610631578063da881e5a1461063b578063dbb06c931461064e578063e30c397814610661578063e4be6e3d14610669578063e4d185db1461067c578063e59e46951461068f578063e6745e13146106a2578063e82f3b70146106b5578063ebf0c717146106c8578063f1650536146106d0578063f2fde38b146106ea578063f379b0df146106fd578063f52fd80314610737578063f6fc05d5146107a8575b600080fd5b6102ab6102a6366004613cfe565b6107b1565b60405190151581526020015b60405180910390f35b6102d36102ce366004613d3d565b6107e8565b005b6102e86102e3366004613d5a565b610927565b6040519081526020016102b7565b6102d3610304366004613d3d565b610961565b6102d3610317366004613dbb565b610a44565b6102d361032a366004613d5a565b610d05565b61034261033d366004613d5a565b610d7f565b6040516102b7929190613ee8565b6102e8600181565b6102e8610366366004613d5a565b610f2f565b6102ab610379366004613f16565b610f7c565b6102ab61038c366004613d3d565b61115d565b6102d361039f366004613d3d565b61120e565b6102ab6103b2366004613d5a565b611308565b6102ab6103c5366004613d3d565b60066020526000908152604090205460ff1681565b6102e86202a30081565b6102ab6103f2366004613f53565b611349565b600d5461040a906001600160a01b031681565b6040516102b79190613f83565b6102d3610425366004613d3d565b61138e565b6102d3611405565b6102d3611429565b61044d610448366004613f97565b611468565b6040805192835263ffffffff9091166020830152016102b7565b60015461040a906001600160a01b031681565b6102d3610488366004613d3d565b61160a565b6102e861049b366004613d5a565b60096020526000908152604090205481565b600454600160281b900464ffffffffff166102e8565b61040a611755565b600b5461040a906001600160a01b031681565b6102ab6104ec366004613d3d565b611770565b6105186104ff366004613d3d565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102b7565b6102e860035481565b61054a610545366004613d5a565b61178e565b6040516102b79190613fcf565b6102ab610565366004613f53565b611827565b6102d3610578366004613d3d565b61186c565b6102e8600e5481565b610599610594366004613d5a565b611903565b6040516102b793929190613fe2565b6102e86210000081565b6102d36105c0366004613d3d565b611a56565b6102e86105d3366004613d5a565b60086020526000908152604090205481565b6001546001600160a01b031661040a565b6102d3611acf565b6102e861060c366004613d5a565b60009081526008602052604090205490565b6102d361062c366004614025565b611b3a565b6102e862093a8081565b6102ab610649366004613d5a565b611c99565b60005461040a906001600160a01b031681565b61040a611f8e565b600c5461040a906001600160a01b031681565b61040a61068a366004614051565b611f99565b6102d361069d366004613d3d565b61203a565b6102d36106b0366004614051565b6120b3565b6102e86106c3366004613d5a565b61227c565b6102e86122ae565b6106d8601481565b60405160ff90911681526020016102b7565b6102d36106f8366004613d3d565b6122c1565b6004546107199064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102b7565b610779610745366004613d5a565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102b7949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e860025481565b60006001600160e01b03198216635a23ad1360e01b14806107e257506001600160e01b031982166301ffc9a760e01b145b92915050565b6107f0611755565b6001600160a01b0316336001600160a01b0316148061081957506001546001600160a01b031633145b61083657604051632864c4e160e01b815260040160405180910390fd5b61083f81611770565b8190610868576040516381e5828960e01b815260040161085f9190613f83565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff16906108979060049083612332565b6001600160a01b0382166000908152600660205260408120805460ff1916905560028054916108c583614089565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d59261091b92869291600160281b900464ffffffffff16906140a0565b60405180910390a25050565b6000818152600a602052604081206004810154610957576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61096961257e565b600c546001600160a01b0316156109925760405162035f5560e61b815260040160405180910390fd5b6001600160a01b0381166109b95760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610a1f57600d80546001600160a01b031981169091556000600e8190556040516001600160a01b039092169182916000805160206149d483398151915291a2505b6040516001600160a01b038216906000805160206149f483398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610a6a57610a6a6140c1565b14610a8857604051634f4b461f60e11b815260040160405180910390fd5b600481015415610aab5760405163632a22bb60e01b815260040160405180910390fd5b85610ac957604051636caad1ed60e11b815260040160405180910390fd5b6000610b3082600601805480602002602001604051908101604052809291908181526020018280548015610b2657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b08575b50505050506125b2565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610b99573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610bc19190810190614275565b9050806101c0015115610c4f57610c4f8b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610c3f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c21575b50505050508c878d8d8d8d6126be565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610c81908e908c906004016143d9565b600060405180830381600087803b158015610c9b57600080fd5b505af1158015610caf573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610cf096959493929190614450565b60405180910390a25050505050505050505050565b610d0d61257e565b60018110158015610d21575062093a808111155b8190610d435760405163028237cd60e61b815260040161085f91815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610db657610db66140d7565b604051908082528060200260200182016040528015610ddf578160200160208202803683370190505b509450806001600160401b03811115610dfa57610dfa6140d7565b604051908082528060200260200182016040528015610e23578160200160208202803683370190505b5093506000805b83811015610f25576000856006018281548110610e4957610e4961449e565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610e9157610e916140c1565b03610f1c5780888481518110610ea957610ea961449e565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610f0357610f0361449e565b602090810291909101015282610f18816144b4565b9350505b50600101610e2a565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610f5457610f546140c1565b03610f7257604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610fa85760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff166003811115610fcd57610fcd6140c1565b14610feb576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015611035573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105991906144cd565b90508061106c60408601602087016144fa565b63ffffffff16111561108460408601602087016144fa565b8290916110b2576040516344ec930f60e01b815263ffffffff9092166004830152602482015260440161085f565b5050815460ff191660019081178355820185905542600283018190556003546110da91614515565b60038301556110ee60058301856002613c0c565b506110f76122ae565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611149928a928a9291614528565b60405180910390a250600195945050505050565b600061116882611770565b61117457506000919050565b6001546001600160a01b031661119d576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906111cd908590600401613f83565b602060405180830381865afa1580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e29190614579565b61121661257e565b600d546001600160a01b0316806112405760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461128157604051630d77758160e31b81526001600160a01b0392831660048201529116602482015260440161085f565b505060006202a300600e546112969190614515565b90508042818110156112bd576040516337c8270b60e01b815260040161085f9291906143d9565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e8190556040516000805160206149f48339815191529190a2505050565b6000818152600a602052604081206001815460ff16600381111561132e5761132e6140c1565b1461133c5750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611386576113866140c1565b149392505050565b61139661257e565b6001600160a01b0381166113bd5760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61140d61257e565b6040516001623f026d60e01b0319815260040160405180910390fd5b3380611433611f8e565b6001600160a01b03161461145c578060405163118cdaa760e01b815260040161085f9190613f83565b61146581612774565b50565b600b5460009081906001600160a01b031633146114985760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff1660038111156114be576114be6140c1565b146114dc57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561151d5761151d6140c1565b1461152d57600b01549150611602565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b820180549161156283614089565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b84986866040516115aa9291906143d9565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b611612611755565b6001600160a01b0316336001600160a01b0316148061163b57506001546001600160a01b031633145b61165857604051632864c4e160e01b815260040160405180910390fd5b61166181611770565b61146557600454600160281b900464ffffffffff16621000008110611699576040516335b4ac3f60e01b815260040160405180910390fd5b6116ad60046001600160a01b03841661279b565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916116ff836144b4565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539261091b92869291600160281b900464ffffffffff16906140a0565b600080611760612916565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a602052604090206004810154606091906117c2576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561181a57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116117fc575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611863576118636140c1565b14159392505050565b61187461257e565b6001600160a01b03811661189b5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f25906118ef906202a30090614515565b60405190815260200160405180910390a250565b60008181526009602052604090205460609081908190611936576040516322e679e360e11b815260040160405180910390fd5b6000848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561199e57602002820191906000526020600020905b81548152602001906001019080831161198a575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156119f057602002820191906000526020600020905b8154815260200190600101908083116119dc575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611a4257602002820191906000526020600020905b815481526020019060010190808311611a2e575b505050505090509250925092509193909250565b611a5e61257e565b6001600160a01b038116611a855760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611ad761257e565b600d546001600160a01b031680611b015760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b038316916000805160206149d483398151915291a250565b6000611b4461293a565b805490915060ff600160401b82041615906001600160401b0316600081158015611b6b5750825b90506000826001600160401b03166001148015611b875750303b155b905081158015611b95575080155b15611bb35760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611bdc57845460ff60401b1916600160401b1785555b6001600160a01b038716611c035760405163d92e233d60e01b815260040160405180910390fd5b611c0c33612963565b611c1860046014612974565b611c2186610d05565b611c29611755565b6001600160a01b0316876001600160a01b031614611c4a57611c4a87612774565b8315611c9057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611cbe57611cbe6140c1565b03611cdc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611cf457611cf46140c1565b14611d1257604051631860f69960e31b815260040160405180910390fd5b80600301544211611d3657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611e22578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b158015611e0057600080fd5b505af1158015611e14573d6000803e3d6000fd5b506000979650505050505050565b611e2b826129b3565b815460ff191660021782556006820154600b83018190556000816001600160401b03811115611e5c57611e5c6140d7565b604051908082528060200260200182016040528015611e85578160200160208202803683370190505b50905060005b82811015611efa57846009016000866006018381548110611eae57611eae61449e565b60009182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611ee757611ee761449e565b6020908102919091010152600101611e8b565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611f4157600080fd5b505af1158015611f55573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e58560060183604051611149929190614594565b600080611760612ae4565b6000828152600a602052604081206002815460ff166003811115611fbf57611fbf6140c1565b14611fdd57604051634f4b461f60e11b815260040160405180910390fd5b60068101548390808210612006576040516326c5c55b60e11b815260040161085f9291906143d9565b505080600601838154811061201d5761201d61449e565b6000918252602090912001546001600160a01b0316949350505050565b61204261257e565b6001600160a01b0381166120695760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156120d8576120d86140c1565b036120f657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561210e5761210e6140c1565b1461212c57604051631860f69960e31b815260040160405180910390fd5b806003015442111561215157604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff16156121845760405163257309f160e11b815260040160405180910390fd5b61218d3361115d565b6121aa5760405163149fbcfd60e11b815260040160405180910390fd5b6121b5338385612b08565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff1916600117905590915061223190839083612ce4565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd858460405161226e9291906143d9565b60405180910390a350505050565b600081815260096020526040902054806122a9576040516322e679e360e11b815260040160405180910390fd5b919050565b60006122bc60046014612ef0565b905090565b6122c961257e565b60006122d3612ae4565b80546001600160a01b0319166001600160a01b03841690811782559091506122f9611755565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614a14833981519152821061234c57600080fd5b825464ffffffffff600160281b9091048116908216811161236c57600080fd5b8260005b818660010160006123818488612f56565b64ffffffffff1681526020019081526020016000208190555060008160016123a991906145a7565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116123de5750612576565b600185166000036124aa5760006123ff836123fa8860016145c0565b612f56565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612461916004016145dd565b602060405180830381865af415801561247e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a291906144cd565b935050612562565b60006124bb836123fa60018961460e565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161251d916004016145dd565b602060405180830381865af415801561253a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061255e91906144cd565b9350505b50647fffffffff600194851c169301612370565b505050505050565b33612587611755565b6001600160a01b0316146125b0573360405163118cdaa760e01b815260040161085f9190613f83565b565b8051600090816125c382601461462b565b6001600160401b038111156125da576125da6140d7565b6040519080825280601f01601f191660200182016040528015612604576020820181803683370190505b50905060005b828110156126ae5760008582815181106126265761262661449e565b602002602001015160601b90506000826014612642919061462b565b905060005b60148110156126a0578281601481106126625761266261449e565b1a60f81b856126718385614515565b815181106126815761268161449e565b60200101906001600160f81b031916908160001a905350600101612647565b50505080600101905061260a565b5080516020909101209392505050565b826126dc57604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b81526004016127199796959493929190614642565b602060405180830381865afa158015612736573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275a9190614579565b506127688a85858585612f74565b50505050505050505050565b600061277e612ae4565b80546001600160a01b03191681559050612797826130b5565b5050565b8154600160281b900464ffffffffff16600080516020614a1483398151915282106127c557600080fd5b825464ffffffffff908116908216106127dd57600080fd5b6127e88160016145c0565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b8185600101600061281f8487612f56565b64ffffffffff168152602081019190915260400160002055600183161561290f576000612851826123fa60018761460e565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916128b3916004016145dd565b602060405180830381865af41580156128d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f491906144cd565b647fffffffff600195861c169490935091909101905061280e565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006107e2565b61296b613111565b61146581613136565b602060ff8216111561298557600080fd5b612996600160ff831681901b61468e565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612adf5760006129d0826001614515565b90505b82811015612ad65760008460060183815481106129f2576129f261449e565b60009182526020822001546006870180546001600160a01b0390921693509084908110612a2157612a2161449e565b6000918252602090912001546001600160a01b0390811691508216811015612acc5780866006018581548110612a5957612a5961449e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612a9d57612a9d61449e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b50506001016129d3565b506001016129bb565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612b295760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612b52576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612b899161468e565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612bd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf691906144cd565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c7191906144cd565b905060008111612c945760405163aeaddff160e01b815260040160405180910390fd5b6000612ca082846146a1565b905060008111612cc35760405163149fbcfd60e11b815260040160405180910390fd5b80861115611c905760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612d6457508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612ee9565b60008087600901600085600081548110612d8057612d8061449e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612e0c576000896009016000878481548110612dcd57612dcd61449e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e03578092508193505b50600101612daa565b50808610612e21576000945050505050612ee9565b600088600a016000868581548110612e3b57612e3b61449e565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612e7957612e796140c1565b021790555086848381548110612e9157612e9161449e565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff1611612f0157600080fd5b602060ff83161115612f1257600080fd5b8254600160281b900464ffffffffff1680612f3160ff851660026147d3565b64ffffffffff161015612f4357600080fd5b612f4e848285613168565b949350505050565b600081612f6a60ff851663ffffffff6147ed565b612ee991906145c0565b80612f9257604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b0316612fbb57604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e90612ffc90309046908d908d908d908d908d90600401614814565b600060405180830381865afa158015613019573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261304191908101906148e5565b60008b8152600f60209081526040909120845194975092955090935061306a9290860190613cae565b506000888152601060209081526040909120835161308a92850190613cae565b50600088815260116020908152604090912082516130aa92840190613cae565b505050505050505050565b60006130bf612916565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b613119613220565b6125b057604051631afcd79f60e31b815260040160405180910390fd5b61313e613111565b6001600160a01b03811661145c576000604051631e4fbdf760e01b815260040161085f9190613f83565b6000602060ff8316111561317b57600080fd5b8264ffffffffff1660000361319a576131938261323a565b9050612ee9565b60006131a78360016145a7565b60ff166001600160401b038111156131c1576131c16140d7565b6040519080825280602002602001820160405280156131ea578160200160208202803683370190505b5090506131f98585858461388f565b808360ff168151811061320e5761320e61449e565b60200260200101519150509392505050565b600061322a61293a565b54600160401b900460ff16919050565b60008160ff1660000361324f57506000919050565b8160ff1660010361328157507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036132b357507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036132e557507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361331757507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361334957507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361337b57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036133ad57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036133df57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361341157507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361344357507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361347557507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c036134a757507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036134d957507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361350b57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361353d57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361356f57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff166011036135a157507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff166012036135d357507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361360557507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361363757507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361366957507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361369b57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff166017036136cd57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036136ff57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361373157507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361376357507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361379557507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c036137c757507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036137f957507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361382b57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361385d57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361029357507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff831611156138a057600080fd5b60008364ffffffffff16116138b457600080fd5b60006138c160018561460e565b905060018116600003613919578460010160006138df600084612f56565b64ffffffffff16815260200190815260200160002054826000815181106139085761390861449e565b602002602001018181525050613943565b613923600061323a565b826000815181106139365761393661449e565b6020026020010181815250505b60005b8360ff168160ff1610156125765760018216600003613a3f5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff16815181106139995761399961449e565b602002602001015181526020016139af8561323a565b8152506040518263ffffffff1660e01b81526004016139ce91906145dd565b602060405180830381865af41580156139eb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a0f91906144cd565b83613a1b8360016145a7565b60ff1681518110613a2e57613a2e61449e565b602002602001018181525050613bf9565b6000613a4c8260016145a7565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613af1576000876001016000613aa5856001613a9491906145a7565b60018864ffffffffff16901c612f56565b64ffffffffff1681526020019081526020016000205490508085846001613acc91906145a7565b60ff1681518110613adf57613adf61449e565b60200260200101818152505050613bf7565b6000876001016000613b0a856001886123fa919061460e565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613b6257613b6261449e565b60200260200101518152506040518263ffffffff1660e01b8152600401613b8991906145dd565b602060405180830381865af4158015613ba6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bca91906144cd565b85613bd68560016145a7565b60ff1681518110613be957613be961449e565b602002602001018181525050505b505b647fffffffff600192831c169101613946565b600183019183908215613c9e5791602002820160005b83821115613c6c57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613c22565b8015613c9c5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613c6c565b505b50613caa929150613ce9565b5090565b828054828255906000526020600020908101928215613c9e579160200282015b82811115613c9e578251825591602001919060010190613cce565b5b80821115613caa5760008155600101613cea565b600060208284031215613d1057600080fd5b81356001600160e01b031981168114612ee957600080fd5b6001600160a01b038116811461146557600080fd5b600060208284031215613d4f57600080fd5b8135612ee981613d28565b600060208284031215613d6c57600080fd5b5035919050565b60008083601f840112613d8557600080fd5b5081356001600160401b03811115613d9c57600080fd5b602083019150836020828501011115613db457600080fd5b9250929050565b60008060008060008060008060a0898b031215613dd757600080fd5b8835975060208901356001600160401b03811115613df457600080fd5b613e008b828c01613d73565b9098509650506040890135945060608901356001600160401b03811115613e2657600080fd5b613e328b828c01613d73565b90955093505060808901356001600160401b03811115613e5157600080fd5b613e5d8b828c01613d73565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b82811015613eac5781516001600160a01b0316865260209586019590910190600101613e85565b5093949350505050565b600081518084526020840193506020830160005b82811015613eac578151865260209586019590910190600101613eca565b604081526000613efb6040830185613e71565b8281036020840152613f0d8185613eb6565b95945050505050565b600080600060808486031215613f2b57600080fd5b833592506020840135915060808401851015613f4657600080fd5b6040840190509250925092565b60008060408385031215613f6657600080fd5b823591506020830135613f7881613d28565b809150509250929050565b6001600160a01b0391909116815260200190565b600080600060608486031215613fac57600080fd5b833592506020840135613fbe81613d28565b929592945050506040919091013590565b602081526000612ee96020830184613e71565b606081526000613ff56060830186613eb6565b82810360208401526140078186613eb6565b9050828103604084015261401b8185613eb6565b9695505050505050565b6000806040838503121561403857600080fd5b823561404381613d28565b946020939093013593505050565b6000806040838503121561406457600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161409857614098614073565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b0381118282101715614110576141106140d7565b60405290565b604051601f8201601f191681016001600160401b038111828210171561413e5761413e6140d7565b604052919050565b8051600481106122a957600080fd5b600082601f83011261416657600080fd5b604080519081016001600160401b0381118282101715614188576141886140d7565b806040525080604084018581111561419f57600080fd5b845b818110156141b95780518352602092830192016141a1565b509195945050505050565b80516122a981613d28565b805160ff811681146122a957600080fd5b600082601f8301126141f157600080fd5b81516001600160401b0381111561420a5761420a6140d7565b61421d601f8201601f1916602001614116565b81815284602083860101111561423257600080fd5b60005b8281101561425157602081860181015183830182015201614235565b506000918101602001919091529392505050565b805180151581146122a957600080fd5b60006020828403121561428757600080fd5b81516001600160401b0381111561429d57600080fd5b820161020081850312156142b057600080fd5b6142b86140ed565b815181526142c860208301614146565b6020820152604082810151908201526142e48560608401614155565b606082015260a082015160808201526142ff60c083016141c4565b60a082015261431060e083016141cf565b60c08201526101008201516001600160401b0381111561432f57600080fd5b61433b868285016141e0565b60e08301525061434e61012083016141c4565b61010082015261436161014083016141c4565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561439857600080fd5b6143a4868285016141e0565b610180830152506143b86101c083016141c4565b6101a08201526143cb6101e08301614265565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b82811015613eac5781546001600160a01b0316865260209095019460019182019101614400565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60808152600061446360808301896143e7565b828103602084015261447681888a614427565b90508560408401528281036060840152614491818587614427565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016144c6576144c6614073565b5060010190565b6000602082840312156144df57600080fd5b5051919050565b803563ffffffff811681146122a957600080fd5b60006020828403121561450c57600080fd5b612ee9826144e6565b808201808211156107e2576107e2614073565b84815260a08101602082018560005b60028110156145645763ffffffff61454e836144e6565b1683526020928301929190910190600101614537565b50505060608201939093526080015292915050565b60006020828403121561458b57600080fd5b612ee982614265565b604081526000613efb60408301856143e7565b60ff81811683821601908111156107e2576107e2614073565b64ffffffffff81811683821601908111156107e2576107e2614073565b60408101818360005b60028110156146055781518352602092830192909101906001016145e6565b50505092915050565b64ffffffffff82811682821603908111156107e2576107e2614073565b80820281158282048414176107e2576107e2614073565b87815286602082015260c06040820152600061466160c0830188613e71565b86606084015285608084015282810360a0840152614680818587614427565b9a9950505050505050505050565b818103818111156107e2576107e2614073565b6000826146be57634e487b7160e01b600052601260045260246000fd5b500490565b6001815b6001841115611602578085048111156146e2576146e2614073565b60018416156146f057908102905b60019390931c9280026146c7565b60008261470d575060016107e2565b8161471a575060006107e2565b8160018114614730576002811461473a5761476c565b60019150506107e2565b60ff84111561474b5761474b614073565b6001841b915064ffffffffff82111561476657614766614073565b506107e2565b5060208310610133831016604e8410600b84101617156147a4575081810a64ffffffffff81111561479f5761479f614073565b6107e2565b6147b464ffffffffff84846146c3565b8064ffffffffff048211156147cb576147cb614073565b029392505050565b6000612ee964ffffffffff841664ffffffffff84166146fe565b64ffffffffff818116838216029081169081811461480d5761480d614073565b5092915050565b60018060a01b038816815286602082015285604082015260a06060820152600061484260a083018688614427565b8281036080840152614680818587614427565b60006001600160401b0382111561486e5761486e6140d7565b5060051b60200190565b600082601f83011261488957600080fd5b815161489c61489782614855565b614116565b8082825260208201915060208360051b8601019250858311156148be57600080fd5b602085015b838110156148db5780518352602092830192016148c3565b5095945050505050565b6000806000606084860312156148fa57600080fd5b83516001600160401b0381111561491057600080fd5b8401601f8101861361492157600080fd5b805161492f61489782614855565b8082825260208201915060208360051b85010192508883111561495157600080fd5b6020840193505b82841015614973578351825260209384019390910190614958565b6020880151909650925050506001600160401b0381111561499357600080fd5b61499f86828701614878565b604086015190935090506001600160401b038111156149bd57600080fd5b6149c986828701614878565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102935760003560e01c806301ffc9a714610298578063096b810a146102c0578063099a161a146102d55780630b45f8e9146102f65780630bbfade7146103095780630f3e34121461031c57806317d611201461032f5780631e08d0e8146103505780632800d82914610358578063291a691b1461036b5780632e7b716d1461037e578063323beaa5146103915780634d6861a6146103a457806350e6d94c146103b757806350e6fad3146103da5780635d504776146103e457806362cc89a8146103f757806370e36bbe14610417578063715018a61461042a57806379ba5097146104325780637c92f5241461043a57806385814243146104675780638a78bb151461047a5780638cb89ecb1461048d5780638d1ddfb1146104ad5780638da5cb5b146104c35780638e5ce3ad146104cb5780639015d371146104de5780639a7a2ffc146104f15780639f0f874a1461052e578063a016493014610537578063a8a4d69b14610557578063acc524941461056a578063b2d5d1ac1461057d578063b8ab470414610586578063bbe4b803146105a8578063bff232c1146105b2578063c2b40ae4146105c5578063c3a0ec30146105e5578063c8fe182d146105f6578063ca2869a0146105fe578063cd6dc6871461061e578063cf90b6ed14610631578063da881e5a1461063b578063dbb06c931461064e578063e30c397814610661578063e4be6e3d14610669578063e4d185db1461067c578063e59e46951461068f578063e6745e13146106a2578063e82f3b70146106b5578063ebf0c717146106c8578063f1650536146106d0578063f2fde38b146106ea578063f379b0df146106fd578063f52fd80314610737578063f6fc05d5146107a8575b600080fd5b6102ab6102a6366004613cfe565b6107b1565b60405190151581526020015b60405180910390f35b6102d36102ce366004613d3d565b6107e8565b005b6102e86102e3366004613d5a565b610927565b6040519081526020016102b7565b6102d3610304366004613d3d565b610961565b6102d3610317366004613dbb565b610a44565b6102d361032a366004613d5a565b610d05565b61034261033d366004613d5a565b610d7f565b6040516102b7929190613ee8565b6102e8600181565b6102e8610366366004613d5a565b610f2f565b6102ab610379366004613f16565b610f7c565b6102ab61038c366004613d3d565b61115d565b6102d361039f366004613d3d565b61120e565b6102ab6103b2366004613d5a565b611308565b6102ab6103c5366004613d3d565b60066020526000908152604090205460ff1681565b6102e86202a30081565b6102ab6103f2366004613f53565b611349565b600d5461040a906001600160a01b031681565b6040516102b79190613f83565b6102d3610425366004613d3d565b61138e565b6102d3611405565b6102d3611429565b61044d610448366004613f97565b611468565b6040805192835263ffffffff9091166020830152016102b7565b60015461040a906001600160a01b031681565b6102d3610488366004613d3d565b61160a565b6102e861049b366004613d5a565b60096020526000908152604090205481565b600454600160281b900464ffffffffff166102e8565b61040a611755565b600b5461040a906001600160a01b031681565b6102ab6104ec366004613d3d565b611770565b6105186104ff366004613d3d565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102b7565b6102e860035481565b61054a610545366004613d5a565b61178e565b6040516102b79190613fcf565b6102ab610565366004613f53565b611827565b6102d3610578366004613d3d565b61186c565b6102e8600e5481565b610599610594366004613d5a565b611903565b6040516102b793929190613fe2565b6102e86210000081565b6102d36105c0366004613d3d565b611a56565b6102e86105d3366004613d5a565b60086020526000908152604090205481565b6001546001600160a01b031661040a565b6102d3611acf565b6102e861060c366004613d5a565b60009081526008602052604090205490565b6102d361062c366004614025565b611b3a565b6102e862093a8081565b6102ab610649366004613d5a565b611c99565b60005461040a906001600160a01b031681565b61040a611f8e565b600c5461040a906001600160a01b031681565b61040a61068a366004614051565b611f99565b6102d361069d366004613d3d565b61203a565b6102d36106b0366004614051565b6120b3565b6102e86106c3366004613d5a565b61227c565b6102e86122ae565b6106d8601481565b60405160ff90911681526020016102b7565b6102d36106f8366004613d3d565b6122c1565b6004546107199064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102b7565b610779610745366004613d5a565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102b7949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102e860025481565b60006001600160e01b03198216635a23ad1360e01b14806107e257506001600160e01b031982166301ffc9a760e01b145b92915050565b6107f0611755565b6001600160a01b0316336001600160a01b0316148061081957506001546001600160a01b031633145b61083657604051632864c4e160e01b815260040160405180910390fd5b61083f81611770565b8190610868576040516381e5828960e01b815260040161085f9190613f83565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff16906108979060049083612332565b6001600160a01b0382166000908152600660205260408120805460ff1916905560028054916108c583614089565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d59261091b92869291600160281b900464ffffffffff16906140a0565b60405180910390a25050565b6000818152600a602052604081206004810154610957576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b61096961257e565b600c546001600160a01b0316156109925760405162035f5560e61b815260040160405180910390fd5b6001600160a01b0381166109b95760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610a1f57600d80546001600160a01b031981169091556000600e8190556040516001600160a01b039092169182916000805160206149d483398151915291a2505b6040516001600160a01b038216906000805160206149f483398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610a6a57610a6a6140c1565b14610a8857604051634f4b461f60e11b815260040160405180910390fd5b600481015415610aab5760405163632a22bb60e01b815260040160405180910390fd5b85610ac957604051636caad1ed60e11b815260040160405180910390fd5b6000610b3082600601805480602002602001604051908101604052809291908181526020018280548015610b2657602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b08575b50505050506125b2565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610b99573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610bc19190810190614275565b9050806101c0015115610c4f57610c4f8b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610c3f57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c21575b50505050508c878d8d8d8d6126be565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610c81908e908c906004016143d9565b600060405180830381600087803b158015610c9b57600080fd5b505af1158015610caf573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610cf096959493929190614450565b60405180910390a25050505050505050505050565b610d0d61257e565b60018110158015610d21575062093a808111155b8190610d435760405163028237cd60e61b815260040161085f91815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610db657610db66140d7565b604051908082528060200260200182016040528015610ddf578160200160208202803683370190505b509450806001600160401b03811115610dfa57610dfa6140d7565b604051908082528060200260200182016040528015610e23578160200160208202803683370190505b5093506000805b83811015610f25576000856006018281548110610e4957610e4961449e565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610e9157610e916140c1565b03610f1c5780888481518110610ea957610ea961449e565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610f0357610f0361449e565b602090810291909101015282610f18816144b4565b9350505b50600101610e2a565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610f5457610f546140c1565b03610f7257604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610fa85760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff166003811115610fcd57610fcd6140c1565b14610feb576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015611035573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105991906144cd565b90508061106c60408601602087016144fa565b63ffffffff16111561108460408601602087016144fa565b8290916110b2576040516344ec930f60e01b815263ffffffff9092166004830152602482015260440161085f565b5050815460ff191660019081178355820185905542600283018190556003546110da91614515565b60038301556110ee60058301856002613c0c565b506110f76122ae565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611149928a928a9291614528565b60405180910390a250600195945050505050565b600061116882611770565b61117457506000919050565b6001546001600160a01b031661119d576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906111cd908590600401613f83565b602060405180830381865afa1580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e29190614579565b61121661257e565b600d546001600160a01b0316806112405760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461128157604051630d77758160e31b81526001600160a01b0392831660048201529116602482015260440161085f565b505060006202a300600e546112969190614515565b90508042818110156112bd576040516337c8270b60e01b815260040161085f9291906143d9565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e8190556040516000805160206149f48339815191529190a2505050565b6000818152600a602052604081206001815460ff16600381111561132e5761132e6140c1565b1461133c5750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611386576113866140c1565b149392505050565b61139661257e565b6001600160a01b0381166113bd5760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61140d61257e565b6040516001623f026d60e01b0319815260040160405180910390fd5b3380611433611f8e565b6001600160a01b03161461145c578060405163118cdaa760e01b815260040161085f9190613f83565b61146581612774565b50565b600b5460009081906001600160a01b031633146114985760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff1660038111156114be576114be6140c1565b146114dc57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561151d5761151d6140c1565b1461152d57600b01549150611602565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b820180549161156283614089565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b84986866040516115aa9291906143d9565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b611612611755565b6001600160a01b0316336001600160a01b0316148061163b57506001546001600160a01b031633145b61165857604051632864c4e160e01b815260040160405180910390fd5b61166181611770565b61146557600454600160281b900464ffffffffff16621000008110611699576040516335b4ac3f60e01b815260040160405180910390fd5b6116ad60046001600160a01b03841661279b565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916116ff836144b4565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539261091b92869291600160281b900464ffffffffff16906140a0565b600080611760612916565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a602052604090206004810154606091906117c2576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561181a57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116117fc575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611863576118636140c1565b14159392505050565b61187461257e565b6001600160a01b03811661189b5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f25906118ef906202a30090614515565b60405190815260200160405180910390a250565b60008181526009602052604090205460609081908190611936576040516322e679e360e11b815260040160405180910390fd5b6000848152600f602090815260408083206010835281842060118452938290208154835181860281018601909452808452919493909291859183018282801561199e57602002820191906000526020600020905b81548152602001906001019080831161198a575b50505050509250818054806020026020016040519081016040528092919081815260200182805480156119f057602002820191906000526020600020905b8154815260200190600101908083116119dc575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611a4257602002820191906000526020600020905b815481526020019060010190808311611a2e575b505050505090509250925092509193909250565b611a5e61257e565b6001600160a01b038116611a855760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611ad761257e565b600d546001600160a01b031680611b015760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b038316916000805160206149d483398151915291a250565b6000611b4461293a565b805490915060ff600160401b82041615906001600160401b0316600081158015611b6b5750825b90506000826001600160401b03166001148015611b875750303b155b905081158015611b95575080155b15611bb35760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611bdc57845460ff60401b1916600160401b1785555b6001600160a01b038716611c035760405163d92e233d60e01b815260040160405180910390fd5b611c0c33612963565b611c1860046014612974565b611c2186610d05565b611c29611755565b6001600160a01b0316876001600160a01b031614611c4a57611c4a87612774565b8315611c9057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611cbe57611cbe6140c1565b03611cdc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611cf457611cf46140c1565b14611d1257604051631860f69960e31b815260040160405180910390fd5b80600301544211611d3657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611e22578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b158015611e0057600080fd5b505af1158015611e14573d6000803e3d6000fd5b506000979650505050505050565b611e2b826129b3565b815460ff191660021782556006820154600b83018190556000816001600160401b03811115611e5c57611e5c6140d7565b604051908082528060200260200182016040528015611e85578160200160208202803683370190505b50905060005b82811015611efa57846009016000866006018381548110611eae57611eae61449e565b60009182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611ee757611ee761449e565b6020908102919091010152600101611e8b565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611f4157600080fd5b505af1158015611f55573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e58560060183604051611149929190614594565b600080611760612ae4565b6000828152600a602052604081206002815460ff166003811115611fbf57611fbf6140c1565b14611fdd57604051634f4b461f60e11b815260040160405180910390fd5b60068101548390808210612006576040516326c5c55b60e11b815260040161085f9291906143d9565b505080600601838154811061201d5761201d61449e565b6000918252602090912001546001600160a01b0316949350505050565b61204261257e565b6001600160a01b0381166120695760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156120d8576120d86140c1565b036120f657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561210e5761210e6140c1565b1461212c57604051631860f69960e31b815260040160405180910390fd5b806003015442111561215157604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff16156121845760405163257309f160e11b815260040160405180910390fd5b61218d3361115d565b6121aa5760405163149fbcfd60e11b815260040160405180910390fd5b6121b5338385612b08565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff1916600117905590915061223190839083612ce4565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd858460405161226e9291906143d9565b60405180910390a350505050565b600081815260096020526040902054806122a9576040516322e679e360e11b815260040160405180910390fd5b919050565b60006122bc60046014612ef0565b905090565b6122c961257e565b60006122d3612ae4565b80546001600160a01b0319166001600160a01b03841690811782559091506122f9611755565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614a14833981519152821061234c57600080fd5b825464ffffffffff600160281b9091048116908216811161236c57600080fd5b8260005b818660010160006123818488612f56565b64ffffffffff1681526020019081526020016000208190555060008160016123a991906145a7565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116123de5750612576565b600185166000036124aa5760006123ff836123fa8860016145c0565b612f56565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612461916004016145dd565b602060405180830381865af415801561247e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a291906144cd565b935050612562565b60006124bb836123fa60018961460e565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161251d916004016145dd565b602060405180830381865af415801561253a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061255e91906144cd565b9350505b50647fffffffff600194851c169301612370565b505050505050565b33612587611755565b6001600160a01b0316146125b0573360405163118cdaa760e01b815260040161085f9190613f83565b565b8051600090816125c382601461462b565b6001600160401b038111156125da576125da6140d7565b6040519080825280601f01601f191660200182016040528015612604576020820181803683370190505b50905060005b828110156126ae5760008582815181106126265761262661449e565b602002602001015160601b90506000826014612642919061462b565b905060005b60148110156126a0578281601481106126625761266261449e565b1a60f81b856126718385614515565b815181106126815761268161449e565b60200101906001600160f81b031916908160001a905350600101612647565b50505080600101905061260a565b5080516020909101209392505050565b826126dc57604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b81526004016127199796959493929190614642565b602060405180830381865afa158015612736573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275a9190614579565b506127688a85858585612f74565b50505050505050505050565b600061277e612ae4565b80546001600160a01b03191681559050612797826130b5565b5050565b8154600160281b900464ffffffffff16600080516020614a1483398151915282106127c557600080fd5b825464ffffffffff908116908216106127dd57600080fd5b6127e88160016145c0565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b8185600101600061281f8487612f56565b64ffffffffff168152602081019190915260400160002055600183161561290f576000612851826123fa60018761460e565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916128b3916004016145dd565b602060405180830381865af41580156128d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f491906144cd565b647fffffffff600195861c169490935091909101905061280e565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006107e2565b61296b613111565b61146581613136565b602060ff8216111561298557600080fd5b612996600160ff831681901b61468e565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612adf5760006129d0826001614515565b90505b82811015612ad65760008460060183815481106129f2576129f261449e565b60009182526020822001546006870180546001600160a01b0390921693509084908110612a2157612a2161449e565b6000918252602090912001546001600160a01b0390811691508216811015612acc5780866006018581548110612a5957612a5961449e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612a9d57612a9d61449e565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b50506001016129d3565b506001016129bb565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612b295760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612b52576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612b899161468e565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612bd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf691906144cd565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c7191906144cd565b905060008111612c945760405163aeaddff160e01b815260040160405180910390fd5b6000612ca082846146a1565b905060008111612cc35760405163149fbcfd60e11b815260040160405180910390fd5b80861115611c905760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612d6457508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612ee9565b60008087600901600085600081548110612d8057612d8061449e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612e0c576000896009016000878481548110612dcd57612dcd61449e565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e03578092508193505b50600101612daa565b50808610612e21576000945050505050612ee9565b600088600a016000868581548110612e3b57612e3b61449e565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612e7957612e796140c1565b021790555086848381548110612e9157612e9161449e565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff1611612f0157600080fd5b602060ff83161115612f1257600080fd5b8254600160281b900464ffffffffff1680612f3160ff851660026147d3565b64ffffffffff161015612f4357600080fd5b612f4e848285613168565b949350505050565b600081612f6a60ff851663ffffffff6147ed565b612ee991906145c0565b80612f9257604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b0316612fbb57604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e90612ffc90309046908d908d908d908d908d90600401614814565b600060405180830381865afa158015613019573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261304191908101906148e5565b60008b8152600f60209081526040909120845194975092955090935061306a9290860190613cae565b506000888152601060209081526040909120835161308a92850190613cae565b50600088815260116020908152604090912082516130aa92840190613cae565b505050505050505050565b60006130bf612916565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b613119613220565b6125b057604051631afcd79f60e31b815260040160405180910390fd5b61313e613111565b6001600160a01b03811661145c576000604051631e4fbdf760e01b815260040161085f9190613f83565b6000602060ff8316111561317b57600080fd5b8264ffffffffff1660000361319a576131938261323a565b9050612ee9565b60006131a78360016145a7565b60ff166001600160401b038111156131c1576131c16140d7565b6040519080825280602002602001820160405280156131ea578160200160208202803683370190505b5090506131f98585858461388f565b808360ff168151811061320e5761320e61449e565b60200260200101519150509392505050565b600061322a61293a565b54600160401b900460ff16919050565b60008160ff1660000361324f57506000919050565b8160ff1660010361328157507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036132b357507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036132e557507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361331757507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361334957507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361337b57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036133ad57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036133df57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361341157507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361344357507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361347557507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c036134a757507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036134d957507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361350b57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f0361353d57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361356f57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff166011036135a157507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff166012036135d357507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361360557507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff1660140361363757507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361366957507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361369b57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff166017036136cd57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff166018036136ff57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff1660190361373157507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a0361376357507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361379557507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c036137c757507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d036137f957507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e0361382b57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f0361385d57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff1660200361029357507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff831611156138a057600080fd5b60008364ffffffffff16116138b457600080fd5b60006138c160018561460e565b905060018116600003613919578460010160006138df600084612f56565b64ffffffffff16815260200190815260200160002054826000815181106139085761390861449e565b602002602001018181525050613943565b613923600061323a565b826000815181106139365761393661449e565b6020026020010181815250505b60005b8360ff168160ff1610156125765760018216600003613a3f5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff16815181106139995761399961449e565b602002602001015181526020016139af8561323a565b8152506040518263ffffffff1660e01b81526004016139ce91906145dd565b602060405180830381865af41580156139eb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a0f91906144cd565b83613a1b8360016145a7565b60ff1681518110613a2e57613a2e61449e565b602002602001018181525050613bf9565b6000613a4c8260016145a7565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613af1576000876001016000613aa5856001613a9491906145a7565b60018864ffffffffff16901c612f56565b64ffffffffff1681526020019081526020016000205490508085846001613acc91906145a7565b60ff1681518110613adf57613adf61449e565b60200260200101818152505050613bf7565b6000876001016000613b0a856001886123fa919061460e565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613b6257613b6261449e565b60200260200101518152506040518263ffffffff1660e01b8152600401613b8991906145dd565b602060405180830381865af4158015613ba6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bca91906144cd565b85613bd68560016145a7565b60ff1681518110613be957613be961449e565b602002602001018181525050505b505b647fffffffff600192831c169101613946565b600183019183908215613c9e5791602002820160005b83821115613c6c57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613c22565b8015613c9c5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613c6c565b505b50613caa929150613ce9565b5090565b828054828255906000526020600020908101928215613c9e579160200282015b82811115613c9e578251825591602001919060010190613cce565b5b80821115613caa5760008155600101613cea565b600060208284031215613d1057600080fd5b81356001600160e01b031981168114612ee957600080fd5b6001600160a01b038116811461146557600080fd5b600060208284031215613d4f57600080fd5b8135612ee981613d28565b600060208284031215613d6c57600080fd5b5035919050565b60008083601f840112613d8557600080fd5b5081356001600160401b03811115613d9c57600080fd5b602083019150836020828501011115613db457600080fd5b9250929050565b60008060008060008060008060a0898b031215613dd757600080fd5b8835975060208901356001600160401b03811115613df457600080fd5b613e008b828c01613d73565b9098509650506040890135945060608901356001600160401b03811115613e2657600080fd5b613e328b828c01613d73565b90955093505060808901356001600160401b03811115613e5157600080fd5b613e5d8b828c01613d73565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b82811015613eac5781516001600160a01b0316865260209586019590910190600101613e85565b5093949350505050565b600081518084526020840193506020830160005b82811015613eac578151865260209586019590910190600101613eca565b604081526000613efb6040830185613e71565b8281036020840152613f0d8185613eb6565b95945050505050565b600080600060808486031215613f2b57600080fd5b833592506020840135915060808401851015613f4657600080fd5b6040840190509250925092565b60008060408385031215613f6657600080fd5b823591506020830135613f7881613d28565b809150509250929050565b6001600160a01b0391909116815260200190565b600080600060608486031215613fac57600080fd5b833592506020840135613fbe81613d28565b929592945050506040919091013590565b602081526000612ee96020830184613e71565b606081526000613ff56060830186613eb6565b82810360208401526140078186613eb6565b9050828103604084015261401b8185613eb6565b9695505050505050565b6000806040838503121561403857600080fd5b823561404381613d28565b946020939093013593505050565b6000806040838503121561406457600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161409857614098614073565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b0381118282101715614110576141106140d7565b60405290565b604051601f8201601f191681016001600160401b038111828210171561413e5761413e6140d7565b604052919050565b8051600481106122a957600080fd5b600082601f83011261416657600080fd5b604080519081016001600160401b0381118282101715614188576141886140d7565b806040525080604084018581111561419f57600080fd5b845b818110156141b95780518352602092830192016141a1565b509195945050505050565b80516122a981613d28565b805160ff811681146122a957600080fd5b600082601f8301126141f157600080fd5b81516001600160401b0381111561420a5761420a6140d7565b61421d601f8201601f1916602001614116565b81815284602083860101111561423257600080fd5b60005b8281101561425157602081860181015183830182015201614235565b506000918101602001919091529392505050565b805180151581146122a957600080fd5b60006020828403121561428757600080fd5b81516001600160401b0381111561429d57600080fd5b820161020081850312156142b057600080fd5b6142b86140ed565b815181526142c860208301614146565b6020820152604082810151908201526142e48560608401614155565b606082015260a082015160808201526142ff60c083016141c4565b60a082015261431060e083016141cf565b60c08201526101008201516001600160401b0381111561432f57600080fd5b61433b868285016141e0565b60e08301525061434e61012083016141c4565b61010082015261436161014083016141c4565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561439857600080fd5b6143a4868285016141e0565b610180830152506143b86101c083016141c4565b6101a08201526143cb6101e08301614265565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b82811015613eac5781546001600160a01b0316865260209095019460019182019101614400565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60808152600061446360808301896143e7565b828103602084015261447681888a614427565b90508560408401528281036060840152614491818587614427565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016144c6576144c6614073565b5060010190565b6000602082840312156144df57600080fd5b5051919050565b803563ffffffff811681146122a957600080fd5b60006020828403121561450c57600080fd5b612ee9826144e6565b808201808211156107e2576107e2614073565b84815260a08101602082018560005b60028110156145645763ffffffff61454e836144e6565b1683526020928301929190910190600101614537565b50505060608201939093526080015292915050565b60006020828403121561458b57600080fd5b612ee982614265565b604081526000613efb60408301856143e7565b60ff81811683821601908111156107e2576107e2614073565b64ffffffffff81811683821601908111156107e2576107e2614073565b60408101818360005b60028110156146055781518352602092830192909101906001016145e6565b50505092915050565b64ffffffffff82811682821603908111156107e2576107e2614073565b80820281158282048414176107e2576107e2614073565b87815286602082015260c06040820152600061466160c0830188613e71565b86606084015285608084015282810360a0840152614680818587614427565b9a9950505050505050505050565b818103818111156107e2576107e2614073565b6000826146be57634e487b7160e01b600052601260045260246000fd5b500490565b6001815b6001841115611602578085048111156146e2576146e2614073565b60018416156146f057908102905b60019390931c9280026146c7565b60008261470d575060016107e2565b8161471a575060006107e2565b8160018114614730576002811461473a5761476c565b60019150506107e2565b60ff84111561474b5761474b614073565b6001841b915064ffffffffff82111561476657614766614073565b506107e2565b5060208310610133831016604e8410600b84101617156147a4575081810a64ffffffffff81111561479f5761479f614073565b6107e2565b6147b464ffffffffff84846146c3565b8064ffffffffff048211156147cb576147cb614073565b029392505050565b6000612ee964ffffffffff841664ffffffffff84166146fe565b64ffffffffff818116838216029081169081811461480d5761480d614073565b5092915050565b60018060a01b038816815286602082015285604082015260a06060820152600061484260a083018688614427565b8281036080840152614680818587614427565b60006001600160401b0382111561486e5761486e6140d7565b5060051b60200190565b600082601f83011261488957600080fd5b815161489c61489782614855565b614116565b8082825260208201915060208360051b8601019250858311156148be57600080fd5b602085015b838110156148db5780518352602092830192016148c3565b5095945050505050565b6000806000606084860312156148fa57600080fd5b83516001600160401b0381111561491057600080fd5b8401601f8101861361492157600080fd5b805161492f61489782614855565b8082825260208201915060208360051b85010192508883111561495157600080fd5b6020840193505b82841015614973578351825260209384019390910190614958565b6020880151909650925050506001600160401b0381111561499357600080fd5b61499f86828701614878565b604086015190935090506001600160401b038111156149bd57600080fd5b6149c986828701614878565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1b30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "bytecode": "0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b614afb806100d96000396000f3fe608060405234801561001057600080fd5b50600436106102b45760003560e01c806301ffc9a7146102b9578063096b810a146102e1578063099a161a146102f65780630b45f8e9146103175780630b88a79a1461032a5780630bbfade7146103335780630f3e34121461034657806317d61120146103595780631e08d0e81461037a5780632800d82914610382578063291a691b146103955780632e7b716d146103a8578063323beaa5146103bb5780634d6861a6146103ce57806350e6d94c146103e157806350e6fad3146104045780635d5047761461040e5780635efb633c1461042157806362cc89a81461042a5780636c120a951461044a57806370e36bbe1461045d578063715018a61461047057806379ba5097146104785780637c92f5241461048057806385814243146104ad5780638a78bb15146104c05780638cb89ecb146104d35780638d1ddfb1146104f35780638da5cb5b146105095780638e5ce3ad146105115780639015d371146105245780639a7a2ffc146105375780639f0f874a14610574578063a01649301461057d578063a8a4d69b1461059d578063acc52494146105b0578063b2d5d1ac146105c3578063b8ab4704146105cc578063bbe4b803146105ee578063bff232c1146105f8578063c2b40ae41461060b578063c3a0ec301461062b578063c8fe182d1461063c578063ca2869a014610644578063cd6dc68714610664578063cf90b6ed14610677578063da881e5a14610681578063dbb06c9314610694578063e30c3978146106a7578063e4be6e3d146106af578063e4d185db146106c2578063e59e4695146106d5578063e6745e13146106e8578063e82f3b70146106fb578063ebf0c7171461070e578063f165053614610716578063f2fde38b14610730578063f379b0df14610743578063f52fd8031461077d578063f6fc05d5146107ee575b600080fd5b6102cc6102c7366004613d99565b6107f7565b60405190151581526020015b60405180910390f35b6102f46102ef366004613dd8565b61082e565b005b610309610304366004613df5565b61096d565b6040519081526020016102d8565b6102f4610325366004613dd8565b6109a7565b61030961070881565b6102f4610341366004613e56565b610a8a565b6102f4610354366004613df5565b610d4b565b61036c610367366004613df5565b610dc6565b6040516102d8929190613f83565b610309600181565b610309610390366004613df5565b610f76565b6102cc6103a3366004613fb1565b610fc3565b6102cc6103b6366004613dd8565b6111a4565b6102f46103c9366004613dd8565b611255565b6102cc6103dc366004613df5565b61134f565b6102cc6103ef366004613dd8565b60066020526000908152604090205460ff1681565b6103096202a30081565b6102cc61041c366004613fee565b611390565b610309600f5481565b600d5461043d906001600160a01b031681565b6040516102d8919061401e565b6102f4610458366004613df5565b6113d5565b6102f461046b366004613dd8565b611400565b6102f4611477565b6102f461149b565b61049361048e366004614032565b6114da565b6040805192835263ffffffff9091166020830152016102d8565b60015461043d906001600160a01b031681565b6102f46104ce366004613dd8565b61167c565b6103096104e1366004613df5565b60096020526000908152604090205481565b600454600160281b900464ffffffffff16610309565b61043d6117c7565b600b5461043d906001600160a01b031681565b6102cc610532366004613dd8565b6117e2565b61055e610545366004613dd8565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102d8565b61030960035481565b61059061058b366004613df5565b611800565b6040516102d8919061406a565b6102cc6105ab366004613fee565b611899565b6102f46105be366004613dd8565b6118de565b610309600e5481565b6105df6105da366004613df5565b611975565b6040516102d89392919061407d565b6103096210000081565b6102f4610606366004613dd8565b611ac8565b610309610619366004613df5565b60086020526000908152604090205481565b6001546001600160a01b031661043d565b6102f4611b41565b610309610652366004613df5565b60009081526008602052604090205490565b6102f46106723660046140c0565b611bac565b61030962093a8081565b6102cc61068f366004613df5565b611d34565b60005461043d906001600160a01b031681565b61043d612029565b600c5461043d906001600160a01b031681565b61043d6106d03660046140ec565b612034565b6102f46106e3366004613dd8565b6120d5565b6102f46106f63660046140ec565b61214e565b610309610709366004613df5565b612317565b610309612349565b61071e601481565b60405160ff90911681526020016102d8565b6102f461073e366004613dd8565b61235c565b60045461075f9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102d8565b6107bf61078b366004613df5565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102d8949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61030960025481565b60006001600160e01b03198216635a23ad1360e01b148061082857506001600160e01b031982166301ffc9a760e01b145b92915050565b6108366117c7565b6001600160a01b0316336001600160a01b0316148061085f57506001546001600160a01b031633145b61087c57604051632864c4e160e01b815260040160405180910390fd5b610885816117e2565b81906108ae576040516381e5828960e01b81526004016108a5919061401e565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff16906108dd90600490836123cd565b6001600160a01b0382166000908152600660205260408120805460ff19169055600280549161090b83614124565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d59261096192869291600160281b900464ffffffffff169061413b565b60405180910390a25050565b6000818152600a60205260408120600481015461099d576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b6109af612619565b600c546001600160a01b0316156109d85760405162035f5560e61b815260040160405180910390fd5b6001600160a01b0381166109ff5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610a6557600d80546001600160a01b031981169091556000600e8190556040516001600160a01b03909216918291600080516020614a6f83398151915291a2505b6040516001600160a01b03821690600080516020614a8f83398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610ab057610ab061415c565b14610ace57604051634f4b461f60e11b815260040160405180910390fd5b600481015415610af15760405163632a22bb60e01b815260040160405180910390fd5b85610b0f57604051636caad1ed60e11b815260040160405180910390fd5b6000610b7682600601805480602002602001604051908101604052809291908181526020018280548015610b6c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b4e575b505050505061264d565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610bdf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c079190810190614310565b9050806101c0015115610c9557610c958b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610c8557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c67575b50505050508c878d8d8d8d612759565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610cc7908e908c90600401614474565b600060405180830381600087803b158015610ce157600080fd5b505af1158015610cf5573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610d36969594939291906144eb565b60405180910390a25050505050505050505050565b610d53612619565b60018110158015610d67575062093a808111155b8190610d895760405163028237cd60e61b81526004016108a591815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab7906020015b60405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610dfd57610dfd614172565b604051908082528060200260200182016040528015610e26578160200160208202803683370190505b509450806001600160401b03811115610e4157610e41614172565b604051908082528060200260200182016040528015610e6a578160200160208202803683370190505b5093506000805b83811015610f6c576000856006018281548110610e9057610e90614539565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610ed857610ed861415c565b03610f635780888481518110610ef057610ef0614539565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610f4a57610f4a614539565b602090810291909101015282610f5f8161454f565b9350505b50600101610e71565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610f9b57610f9b61415c565b03610fb957604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610fef5760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff1660038111156110145761101461415c565b14611032576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa15801561107c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a09190614568565b9050806110b36040860160208701614595565b63ffffffff1611156110cb6040860160208701614595565b8290916110f9576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016108a5565b5050815460ff19166001908117835582018590554260028301819055600354611121916145b0565b600383015561113560058301856002613ca7565b5061113e612349565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611190928a928a92916145c3565b60405180910390a250600195945050505050565b60006111af826117e2565b6111bb57506000919050565b6001546001600160a01b03166111e4576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d79061121490859060040161401e565b602060405180830381865afa158015611231573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108289190614614565b61125d612619565b600d546001600160a01b0316806112875760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b03828116908216146112c857604051630d77758160e31b81526001600160a01b039283166004820152911660248201526044016108a5565b505060006202a300600e546112dd91906145b0565b9050804281811015611304576040516337c8270b60e01b81526004016108a5929190614474565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e819055604051600080516020614a8f8339815191529190a2505050565b6000818152600a602052604081206001815460ff1660038111156113755761137561415c565b146113835750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156113cd576113cd61415c565b149392505050565b6113dd612619565b600f819055604051818152600080516020614aaf83398151915290602001610dbb565b611408612619565b6001600160a01b03811661142f5760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61147f612619565b6040516001623f026d60e01b0319815260040160405180910390fd5b33806114a5612029565b6001600160a01b0316146114ce578060405163118cdaa760e01b81526004016108a5919061401e565b6114d78161280f565b50565b600b5460009081906001600160a01b0316331461150a5760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff1660038111156115305761153061415c565b1461154e57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561158f5761158f61415c565b1461159f57600b01549150611674565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b82018054916115d483614124565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161161c929190614474565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6116846117c7565b6001600160a01b0316336001600160a01b031614806116ad57506001546001600160a01b031633145b6116ca57604051632864c4e160e01b815260040160405180910390fd5b6116d3816117e2565b6114d757600454600160281b900464ffffffffff1662100000811061170b576040516335b4ac3f60e01b815260040160405180910390fd5b61171f60046001600160a01b038416612836565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916117718361454f565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539261096192869291600160281b900464ffffffffff169061413b565b6000806117d26129b1565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a60205260409020600481015460609190611834576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561188c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161186e575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156118d5576118d561415c565b14159392505050565b6118e6612619565b6001600160a01b03811661190d5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611961906202a300906145b0565b60405190815260200160405180910390a250565b600081815260096020526040902054606090819081906119a8576040516322e679e360e11b815260040160405180910390fd5b600084815260106020908152604080832060118352818420601284529382902081548351818602810186019094528084529194939092918591830182828015611a1057602002820191906000526020600020905b8154815260200190600101908083116119fc575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611a6257602002820191906000526020600020905b815481526020019060010190808311611a4e575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611ab457602002820191906000526020600020905b815481526020019060010190808311611aa0575b505050505090509250925092509193909250565b611ad0612619565b6001600160a01b038116611af75760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611b49612619565b600d546001600160a01b031680611b735760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b03831691600080516020614a6f83398151915291a250565b6000611bb66129d5565b805490915060ff600160401b82041615906001600160401b0316600081158015611bdd5750825b90506000826001600160401b03166001148015611bf95750303b155b905081158015611c07575080155b15611c255760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611c4e57845460ff60401b1916600160401b1785555b6001600160a01b038716611c755760405163d92e233d60e01b815260040160405180910390fd5b611c7e336129fe565b611c8a60046014612a0f565b611c9386610d4b565b610708600f819055604051908152600080516020614aaf8339815191529060200160405180910390a1611cc46117c7565b6001600160a01b0316876001600160a01b031614611ce557611ce58761280f565b8315611d2b57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611d5957611d5961415c565b03611d7757604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611d8f57611d8f61415c565b14611dad57604051631860f69960e31b815260040160405180910390fd5b80600301544211611dd157604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611ebd578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b158015611e9b57600080fd5b505af1158015611eaf573d6000803e3d6000fd5b506000979650505050505050565b611ec682612a4e565b815460ff191660021782556006820154600b83018190556000816001600160401b03811115611ef757611ef7614172565b604051908082528060200260200182016040528015611f20578160200160208202803683370190505b50905060005b82811015611f9557846009016000866006018381548110611f4957611f49614539565b60009182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611f8257611f82614539565b6020908102919091010152600101611f26565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611fdc57600080fd5b505af1158015611ff0573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e5856006018360405161119092919061462f565b6000806117d2612b7f565b6000828152600a602052604081206002815460ff16600381111561205a5761205a61415c565b1461207857604051634f4b461f60e11b815260040160405180910390fd5b600681015483908082106120a1576040516326c5c55b60e11b81526004016108a5929190614474565b50508060060183815481106120b8576120b8614539565b6000918252602090912001546001600160a01b0316949350505050565b6120dd612619565b6001600160a01b0381166121045760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156121735761217361415c565b0361219157604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156121a9576121a961415c565b146121c757604051631860f69960e31b815260040160405180910390fd5b80600301544211156121ec57604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff161561221f5760405163257309f160e11b815260040160405180910390fd5b612228336111a4565b6122455760405163149fbcfd60e11b815260040160405180910390fd5b612250338385612ba3565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff191660011790559091506122cc90839083612d7f565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd8584604051612309929190614474565b60405180910390a350505050565b60008181526009602052604090205480612344576040516322e679e360e11b815260040160405180910390fd5b919050565b600061235760046014612f8b565b905090565b612364612619565b600061236e612b7f565b80546001600160a01b0319166001600160a01b03841690811782559091506123946117c7565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614acf83398151915282106123e757600080fd5b825464ffffffffff600160281b9091048116908216811161240757600080fd5b8260005b8186600101600061241c8488612ff1565b64ffffffffff1681526020019081526020016000208190555060008160016124449190614642565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116124795750612611565b6001851660000361254557600061249a8361249588600161465b565b612ff1565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916124fc91600401614678565b602060405180830381865af4158015612519573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253d9190614568565b9350506125fd565b6000612556836124956001896146a9565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916125b891600401614678565b602060405180830381865af41580156125d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f99190614568565b9350505b50647fffffffff600194851c16930161240b565b505050505050565b336126226117c7565b6001600160a01b03161461264b573360405163118cdaa760e01b81526004016108a5919061401e565b565b80516000908161265e8260146146c6565b6001600160401b0381111561267557612675614172565b6040519080825280601f01601f19166020018201604052801561269f576020820181803683370190505b50905060005b828110156127495760008582815181106126c1576126c1614539565b602002602001015160601b905060008260146126dd91906146c6565b905060005b601481101561273b578281601481106126fd576126fd614539565b1a60f81b8561270c83856145b0565b8151811061271c5761271c614539565b60200101906001600160f81b031916908160001a9053506001016126e2565b5050508060010190506126a5565b5080516020909101209392505050565b8261277757604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b81526004016127b497969594939291906146dd565b602060405180830381865afa1580156127d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f59190614614565b506128038a8585858561300f565b50505050505050505050565b6000612819612b7f565b80546001600160a01b0319168155905061283282613150565b5050565b8154600160281b900464ffffffffff16600080516020614acf833981519152821061286057600080fd5b825464ffffffffff9081169082161061287857600080fd5b61288381600161465b565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b818560010160006128ba8487612ff1565b64ffffffffff16815260208101919091526040016000205560018316156129aa5760006128ec826124956001876146a9565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161294e91600401614678565b602060405180830381865af415801561296b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061298f9190614568565b647fffffffff600195861c16949093509190910190506128a9565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610828565b612a066131ac565b6114d7816131d1565b602060ff82161115612a2057600080fd5b612a31600160ff831681901b614729565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612b7a576000612a6b8260016145b0565b90505b82811015612b71576000846006018381548110612a8d57612a8d614539565b60009182526020822001546006870180546001600160a01b0390921693509084908110612abc57612abc614539565b6000918252602090912001546001600160a01b0390811691508216811015612b675780866006018581548110612af457612af4614539565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612b3857612b38614539565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612a6e565b50600101612a56565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612bc45760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612bed576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612c2491614729565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c919190614568565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0c9190614568565b905060008111612d2f5760405163aeaddff160e01b815260040160405180910390fd5b6000612d3b828461473c565b905060008111612d5e5760405163149fbcfd60e11b815260040160405180910390fd5b80861115611d2b5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612dff57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612f84565b60008087600901600085600081548110612e1b57612e1b614539565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612ea7576000896009016000878481548110612e6857612e68614539565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e9e578092508193505b50600101612e45565b50808610612ebc576000945050505050612f84565b600088600a016000868581548110612ed657612ed6614539565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612f1457612f1461415c565b021790555086848381548110612f2c57612f2c614539565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff1611612f9c57600080fd5b602060ff83161115612fad57600080fd5b8254600160281b900464ffffffffff1680612fcc60ff8516600261486e565b64ffffffffff161015612fde57600080fd5b612fe9848285613203565b949350505050565b60008161300560ff851663ffffffff614888565b612f84919061465b565b8061302d57604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661305657604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e9061309790309046908d908d908d908d908d906004016148af565b600060405180830381865afa1580156130b4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526130dc9190810190614980565b60008b815260106020908152604090912084519497509295509093506131059290860190613d49565b506000888152601160209081526040909120835161312592850190613d49565b506000888152601260209081526040909120825161314592840190613d49565b505050505050505050565b600061315a6129b1565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6131b46132bb565b61264b57604051631afcd79f60e31b815260040160405180910390fd5b6131d96131ac565b6001600160a01b0381166114ce576000604051631e4fbdf760e01b81526004016108a5919061401e565b6000602060ff8316111561321657600080fd5b8264ffffffffff166000036132355761322e826132d5565b9050612f84565b6000613242836001614642565b60ff166001600160401b0381111561325c5761325c614172565b604051908082528060200260200182016040528015613285578160200160208202803683370190505b5090506132948585858461392a565b808360ff16815181106132a9576132a9614539565b60200260200101519150509392505050565b60006132c56129d5565b54600160401b900460ff16919050565b60008160ff166000036132ea57506000919050565b8160ff1660010361331c57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361334e57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361338057507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036133b257507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036133e457507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361341657507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361344857507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361347a57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036134ac57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036134de57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361351057507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361354257507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361357457507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036135a657507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036135d857507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361360a57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361363c57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361366e57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036136a057507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036136d257507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361370457507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361373657507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361376857507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361379a57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036137cc57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036137fe57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361383057507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361386257507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361389457507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036138c657507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f036138f857507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036102b457507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff8316111561393b57600080fd5b60008364ffffffffff161161394f57600080fd5b600061395c6001856146a9565b9050600181166000036139b45784600101600061397a600084612ff1565b64ffffffffff16815260200190815260200160002054826000815181106139a3576139a3614539565b6020026020010181815250506139de565b6139be60006132d5565b826000815181106139d1576139d1614539565b6020026020010181815250505b60005b8360ff168160ff1610156126115760018216600003613ada5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110613a3457613a34614539565b60200260200101518152602001613a4a856132d5565b8152506040518263ffffffff1660e01b8152600401613a699190614678565b602060405180830381865af4158015613a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aaa9190614568565b83613ab6836001614642565b60ff1681518110613ac957613ac9614539565b602002602001018181525050613c94565b6000613ae7826001614642565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613b8c576000876001016000613b40856001613b2f9190614642565b60018864ffffffffff16901c612ff1565b64ffffffffff1681526020019081526020016000205490508085846001613b679190614642565b60ff1681518110613b7a57613b7a614539565b60200260200101818152505050613c92565b6000876001016000613ba58560018861249591906146a9565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613bfd57613bfd614539565b60200260200101518152506040518263ffffffff1660e01b8152600401613c249190614678565b602060405180830381865af4158015613c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c659190614568565b85613c71856001614642565b60ff1681518110613c8457613c84614539565b602002602001018181525050505b505b647fffffffff600192831c1691016139e1565b600183019183908215613d395791602002820160005b83821115613d0757833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613cbd565b8015613d375782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613d07565b505b50613d45929150613d84565b5090565b828054828255906000526020600020908101928215613d39579160200282015b82811115613d39578251825591602001919060010190613d69565b5b80821115613d455760008155600101613d85565b600060208284031215613dab57600080fd5b81356001600160e01b031981168114612f8457600080fd5b6001600160a01b03811681146114d757600080fd5b600060208284031215613dea57600080fd5b8135612f8481613dc3565b600060208284031215613e0757600080fd5b5035919050565b60008083601f840112613e2057600080fd5b5081356001600160401b03811115613e3757600080fd5b602083019150836020828501011115613e4f57600080fd5b9250929050565b60008060008060008060008060a0898b031215613e7257600080fd5b8835975060208901356001600160401b03811115613e8f57600080fd5b613e9b8b828c01613e0e565b9098509650506040890135945060608901356001600160401b03811115613ec157600080fd5b613ecd8b828c01613e0e565b90955093505060808901356001600160401b03811115613eec57600080fd5b613ef88b828c01613e0e565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b82811015613f475781516001600160a01b0316865260209586019590910190600101613f20565b5093949350505050565b600081518084526020840193506020830160005b82811015613f47578151865260209586019590910190600101613f65565b604081526000613f966040830185613f0c565b8281036020840152613fa88185613f51565b95945050505050565b600080600060808486031215613fc657600080fd5b833592506020840135915060808401851015613fe157600080fd5b6040840190509250925092565b6000806040838503121561400157600080fd5b82359150602083013561401381613dc3565b809150509250929050565b6001600160a01b0391909116815260200190565b60008060006060848603121561404757600080fd5b83359250602084013561405981613dc3565b929592945050506040919091013590565b602081526000612f846020830184613f0c565b6060815260006140906060830186613f51565b82810360208401526140a28186613f51565b905082810360408401526140b68185613f51565b9695505050505050565b600080604083850312156140d357600080fd5b82356140de81613dc3565b946020939093013593505050565b600080604083850312156140ff57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6000816141335761413361410e565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b03811182821017156141ab576141ab614172565b60405290565b604051601f8201601f191681016001600160401b03811182821017156141d9576141d9614172565b604052919050565b80516004811061234457600080fd5b600082601f83011261420157600080fd5b604080519081016001600160401b038111828210171561422357614223614172565b806040525080604084018581111561423a57600080fd5b845b8181101561425457805183526020928301920161423c565b509195945050505050565b805161234481613dc3565b805160ff8116811461234457600080fd5b600082601f83011261428c57600080fd5b81516001600160401b038111156142a5576142a5614172565b6142b8601f8201601f19166020016141b1565b8181528460208386010111156142cd57600080fd5b60005b828110156142ec576020818601810151838301820152016142d0565b506000918101602001919091529392505050565b8051801515811461234457600080fd5b60006020828403121561432257600080fd5b81516001600160401b0381111561433857600080fd5b8201610200818503121561434b57600080fd5b614353614188565b81518152614363602083016141e1565b60208201526040828101519082015261437f85606084016141f0565b606082015260a0820151608082015261439a60c0830161425f565b60a08201526143ab60e0830161426a565b60c08201526101008201516001600160401b038111156143ca57600080fd5b6143d68682850161427b565b60e0830152506143e9610120830161425f565b6101008201526143fc610140830161425f565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561443357600080fd5b61443f8682850161427b565b610180830152506144536101c0830161425f565b6101a08201526144666101e08301614300565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b82811015613f475781546001600160a01b031686526020909501946001918201910161449b565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6080815260006144fe6080830189614482565b828103602084015261451181888a6144c2565b9050856040840152828103606084015261452c8185876144c2565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016145615761456161410e565b5060010190565b60006020828403121561457a57600080fd5b5051919050565b803563ffffffff8116811461234457600080fd5b6000602082840312156145a757600080fd5b612f8482614581565b808201808211156108285761082861410e565b84815260a08101602082018560005b60028110156145ff5763ffffffff6145e983614581565b16835260209283019291909101906001016145d2565b50505060608201939093526080015292915050565b60006020828403121561462657600080fd5b612f8482614300565b604081526000613f966040830185614482565b60ff81811683821601908111156108285761082861410e565b64ffffffffff81811683821601908111156108285761082861410e565b60408101818360005b60028110156146a0578151835260209283019290910190600101614681565b50505092915050565b64ffffffffff82811682821603908111156108285761082861410e565b80820281158282048414176108285761082861410e565b87815286602082015260c0604082015260006146fc60c0830188613f0c565b86606084015285608084015282810360a084015261471b8185876144c2565b9a9950505050505050505050565b818103818111156108285761082861410e565b60008261475957634e487b7160e01b600052601260045260246000fd5b500490565b6001815b60018411156116745780850481111561477d5761477d61410e565b600184161561478b57908102905b60019390931c928002614762565b6000826147a857506001610828565b816147b557506000610828565b81600181146147cb57600281146147d557614807565b6001915050610828565b60ff8411156147e6576147e661410e565b6001841b915064ffffffffff8211156148015761480161410e565b50610828565b5060208310610133831016604e8410600b841016171561483f575081810a64ffffffffff81111561483a5761483a61410e565b610828565b61484f64ffffffffff848461475e565b8064ffffffffff048211156148665761486661410e565b029392505050565b6000612f8464ffffffffff841664ffffffffff8416614799565b64ffffffffff81811683821602908116908181146148a8576148a861410e565b5092915050565b60018060a01b038816815286602082015285604082015260a0606082015260006148dd60a0830186886144c2565b828103608084015261471b8185876144c2565b60006001600160401b0382111561490957614909614172565b5060051b60200190565b600082601f83011261492457600080fd5b8151614937614932826148f0565b6141b1565b8082825260208201915060208360051b86010192508583111561495957600080fd5b602085015b8381101561497657805183526020928301920161495e565b5095945050505050565b60008060006060848603121561499557600080fd5b83516001600160401b038111156149ab57600080fd5b8401601f810186136149bc57600080fd5b80516149ca614932826148f0565b8082825260208201915060208360051b8501019250888311156149ec57600080fd5b6020840193505b82841015614a0e5783518252602093840193909101906149f3565b6020880151909650925050506001600160401b03811115614a2e57600080fd5b614a3a86828701614913565b604086015190935090506001600160401b03811115614a5857600080fd5b614a6486828701614913565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1bdd568bd039595fd4624b89bd697f998c689807d04c17303be9d1417adbdc056f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102b45760003560e01c806301ffc9a7146102b9578063096b810a146102e1578063099a161a146102f65780630b45f8e9146103175780630b88a79a1461032a5780630bbfade7146103335780630f3e34121461034657806317d61120146103595780631e08d0e81461037a5780632800d82914610382578063291a691b146103955780632e7b716d146103a8578063323beaa5146103bb5780634d6861a6146103ce57806350e6d94c146103e157806350e6fad3146104045780635d5047761461040e5780635efb633c1461042157806362cc89a81461042a5780636c120a951461044a57806370e36bbe1461045d578063715018a61461047057806379ba5097146104785780637c92f5241461048057806385814243146104ad5780638a78bb15146104c05780638cb89ecb146104d35780638d1ddfb1146104f35780638da5cb5b146105095780638e5ce3ad146105115780639015d371146105245780639a7a2ffc146105375780639f0f874a14610574578063a01649301461057d578063a8a4d69b1461059d578063acc52494146105b0578063b2d5d1ac146105c3578063b8ab4704146105cc578063bbe4b803146105ee578063bff232c1146105f8578063c2b40ae41461060b578063c3a0ec301461062b578063c8fe182d1461063c578063ca2869a014610644578063cd6dc68714610664578063cf90b6ed14610677578063da881e5a14610681578063dbb06c9314610694578063e30c3978146106a7578063e4be6e3d146106af578063e4d185db146106c2578063e59e4695146106d5578063e6745e13146106e8578063e82f3b70146106fb578063ebf0c7171461070e578063f165053614610716578063f2fde38b14610730578063f379b0df14610743578063f52fd8031461077d578063f6fc05d5146107ee575b600080fd5b6102cc6102c7366004613d99565b6107f7565b60405190151581526020015b60405180910390f35b6102f46102ef366004613dd8565b61082e565b005b610309610304366004613df5565b61096d565b6040519081526020016102d8565b6102f4610325366004613dd8565b6109a7565b61030961070881565b6102f4610341366004613e56565b610a8a565b6102f4610354366004613df5565b610d4b565b61036c610367366004613df5565b610dc6565b6040516102d8929190613f83565b610309600181565b610309610390366004613df5565b610f76565b6102cc6103a3366004613fb1565b610fc3565b6102cc6103b6366004613dd8565b6111a4565b6102f46103c9366004613dd8565b611255565b6102cc6103dc366004613df5565b61134f565b6102cc6103ef366004613dd8565b60066020526000908152604090205460ff1681565b6103096202a30081565b6102cc61041c366004613fee565b611390565b610309600f5481565b600d5461043d906001600160a01b031681565b6040516102d8919061401e565b6102f4610458366004613df5565b6113d5565b6102f461046b366004613dd8565b611400565b6102f4611477565b6102f461149b565b61049361048e366004614032565b6114da565b6040805192835263ffffffff9091166020830152016102d8565b60015461043d906001600160a01b031681565b6102f46104ce366004613dd8565b61167c565b6103096104e1366004613df5565b60096020526000908152604090205481565b600454600160281b900464ffffffffff16610309565b61043d6117c7565b600b5461043d906001600160a01b031681565b6102cc610532366004613dd8565b6117e2565b61055e610545366004613dd8565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102d8565b61030960035481565b61059061058b366004613df5565b611800565b6040516102d8919061406a565b6102cc6105ab366004613fee565b611899565b6102f46105be366004613dd8565b6118de565b610309600e5481565b6105df6105da366004613df5565b611975565b6040516102d89392919061407d565b6103096210000081565b6102f4610606366004613dd8565b611ac8565b610309610619366004613df5565b60086020526000908152604090205481565b6001546001600160a01b031661043d565b6102f4611b41565b610309610652366004613df5565b60009081526008602052604090205490565b6102f46106723660046140c0565b611bac565b61030962093a8081565b6102cc61068f366004613df5565b611d34565b60005461043d906001600160a01b031681565b61043d612029565b600c5461043d906001600160a01b031681565b61043d6106d03660046140ec565b612034565b6102f46106e3366004613dd8565b6120d5565b6102f46106f63660046140ec565b61214e565b610309610709366004613df5565b612317565b610309612349565b61071e601481565b60405160ff90911681526020016102d8565b6102f461073e366004613dd8565b61235c565b60045461075f9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102d8565b6107bf61078b366004613df5565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102d8949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61030960025481565b60006001600160e01b03198216635a23ad1360e01b148061082857506001600160e01b031982166301ffc9a760e01b145b92915050565b6108366117c7565b6001600160a01b0316336001600160a01b0316148061085f57506001546001600160a01b031633145b61087c57604051632864c4e160e01b815260040160405180910390fd5b610885816117e2565b81906108ae576040516381e5828960e01b81526004016108a5919061401e565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff16906108dd90600490836123cd565b6001600160a01b0382166000908152600660205260408120805460ff19169055600280549161090b83614124565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d59261096192869291600160281b900464ffffffffff169061413b565b60405180910390a25050565b6000818152600a60205260408120600481015461099d576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b6109af612619565b600c546001600160a01b0316156109d85760405162035f5560e61b815260040160405180910390fd5b6001600160a01b0381166109ff5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610a6557600d80546001600160a01b031981169091556000600e8190556040516001600160a01b03909216918291600080516020614a6f83398151915291a2505b6040516001600160a01b03821690600080516020614a8f83398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610ab057610ab061415c565b14610ace57604051634f4b461f60e11b815260040160405180910390fd5b600481015415610af15760405163632a22bb60e01b815260040160405180910390fd5b85610b0f57604051636caad1ed60e11b815260040160405180910390fd5b6000610b7682600601805480602002602001604051908101604052809291908181526020018280548015610b6c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b4e575b505050505061264d565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610bdf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c079190810190614310565b9050806101c0015115610c9557610c958b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610c8557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c67575b50505050508c878d8d8d8d612759565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610cc7908e908c90600401614474565b600060405180830381600087803b158015610ce157600080fd5b505af1158015610cf5573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610d36969594939291906144eb565b60405180910390a25050505050505050505050565b610d53612619565b60018110158015610d67575062093a808111155b8190610d895760405163028237cd60e61b81526004016108a591815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab7906020015b60405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610dfd57610dfd614172565b604051908082528060200260200182016040528015610e26578160200160208202803683370190505b509450806001600160401b03811115610e4157610e41614172565b604051908082528060200260200182016040528015610e6a578160200160208202803683370190505b5093506000805b83811015610f6c576000856006018281548110610e9057610e90614539565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610ed857610ed861415c565b03610f635780888481518110610ef057610ef0614539565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610f4a57610f4a614539565b602090810291909101015282610f5f8161454f565b9350505b50600101610e71565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610f9b57610f9b61415c565b03610fb957604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610fef5760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff1660038111156110145761101461415c565b14611032576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa15801561107c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a09190614568565b9050806110b36040860160208701614595565b63ffffffff1611156110cb6040860160208701614595565b8290916110f9576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016108a5565b5050815460ff19166001908117835582018590554260028301819055600354611121916145b0565b600383015561113560058301856002613ca7565b5061113e612349565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611190928a928a92916145c3565b60405180910390a250600195945050505050565b60006111af826117e2565b6111bb57506000919050565b6001546001600160a01b03166111e4576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d79061121490859060040161401e565b602060405180830381865afa158015611231573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108289190614614565b61125d612619565b600d546001600160a01b0316806112875760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b03828116908216146112c857604051630d77758160e31b81526001600160a01b039283166004820152911660248201526044016108a5565b505060006202a300600e546112dd91906145b0565b9050804281811015611304576040516337c8270b60e01b81526004016108a5929190614474565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e819055604051600080516020614a8f8339815191529190a2505050565b6000818152600a602052604081206001815460ff1660038111156113755761137561415c565b146113835750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156113cd576113cd61415c565b149392505050565b6113dd612619565b600f819055604051818152600080516020614aaf83398151915290602001610dbb565b611408612619565b6001600160a01b03811661142f5760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61147f612619565b6040516001623f026d60e01b0319815260040160405180910390fd5b33806114a5612029565b6001600160a01b0316146114ce578060405163118cdaa760e01b81526004016108a5919061401e565b6114d78161280f565b50565b600b5460009081906001600160a01b0316331461150a5760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff1660038111156115305761153061415c565b1461154e57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561158f5761158f61415c565b1461159f57600b01549150611674565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b82018054916115d483614124565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161161c929190614474565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6116846117c7565b6001600160a01b0316336001600160a01b031614806116ad57506001546001600160a01b031633145b6116ca57604051632864c4e160e01b815260040160405180910390fd5b6116d3816117e2565b6114d757600454600160281b900464ffffffffff1662100000811061170b576040516335b4ac3f60e01b815260040160405180910390fd5b61171f60046001600160a01b038416612836565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916117718361454f565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539261096192869291600160281b900464ffffffffff169061413b565b6000806117d26129b1565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a60205260409020600481015460609190611834576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561188c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161186e575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156118d5576118d561415c565b14159392505050565b6118e6612619565b6001600160a01b03811661190d5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611961906202a300906145b0565b60405190815260200160405180910390a250565b600081815260096020526040902054606090819081906119a8576040516322e679e360e11b815260040160405180910390fd5b600084815260106020908152604080832060118352818420601284529382902081548351818602810186019094528084529194939092918591830182828015611a1057602002820191906000526020600020905b8154815260200190600101908083116119fc575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611a6257602002820191906000526020600020905b815481526020019060010190808311611a4e575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611ab457602002820191906000526020600020905b815481526020019060010190808311611aa0575b505050505090509250925092509193909250565b611ad0612619565b6001600160a01b038116611af75760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611b49612619565b600d546001600160a01b031680611b735760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b03831691600080516020614a6f83398151915291a250565b6000611bb66129d5565b805490915060ff600160401b82041615906001600160401b0316600081158015611bdd5750825b90506000826001600160401b03166001148015611bf95750303b155b905081158015611c07575080155b15611c255760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611c4e57845460ff60401b1916600160401b1785555b6001600160a01b038716611c755760405163d92e233d60e01b815260040160405180910390fd5b611c7e336129fe565b611c8a60046014612a0f565b611c9386610d4b565b610708600f819055604051908152600080516020614aaf8339815191529060200160405180910390a1611cc46117c7565b6001600160a01b0316876001600160a01b031614611ce557611ce58761280f565b8315611d2b57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611d5957611d5961415c565b03611d7757604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611d8f57611d8f61415c565b14611dad57604051631860f69960e31b815260040160405180910390fd5b80600301544211611dd157604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611ebd578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b158015611e9b57600080fd5b505af1158015611eaf573d6000803e3d6000fd5b506000979650505050505050565b611ec682612a4e565b815460ff191660021782556006820154600b83018190556000816001600160401b03811115611ef757611ef7614172565b604051908082528060200260200182016040528015611f20578160200160208202803683370190505b50905060005b82811015611f9557846009016000866006018381548110611f4957611f49614539565b60009182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611f8257611f82614539565b6020908102919091010152600101611f26565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611fdc57600080fd5b505af1158015611ff0573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e5856006018360405161119092919061462f565b6000806117d2612b7f565b6000828152600a602052604081206002815460ff16600381111561205a5761205a61415c565b1461207857604051634f4b461f60e11b815260040160405180910390fd5b600681015483908082106120a1576040516326c5c55b60e11b81526004016108a5929190614474565b50508060060183815481106120b8576120b8614539565b6000918252602090912001546001600160a01b0316949350505050565b6120dd612619565b6001600160a01b0381166121045760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156121735761217361415c565b0361219157604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156121a9576121a961415c565b146121c757604051631860f69960e31b815260040160405180910390fd5b80600301544211156121ec57604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff161561221f5760405163257309f160e11b815260040160405180910390fd5b612228336111a4565b6122455760405163149fbcfd60e11b815260040160405180910390fd5b612250338385612ba3565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff191660011790559091506122cc90839083612d7f565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd8584604051612309929190614474565b60405180910390a350505050565b60008181526009602052604090205480612344576040516322e679e360e11b815260040160405180910390fd5b919050565b600061235760046014612f8b565b905090565b612364612619565b600061236e612b7f565b80546001600160a01b0319166001600160a01b03841690811782559091506123946117c7565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614acf83398151915282106123e757600080fd5b825464ffffffffff600160281b9091048116908216811161240757600080fd5b8260005b8186600101600061241c8488612ff1565b64ffffffffff1681526020019081526020016000208190555060008160016124449190614642565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116124795750612611565b6001851660000361254557600061249a8361249588600161465b565b612ff1565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916124fc91600401614678565b602060405180830381865af4158015612519573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253d9190614568565b9350506125fd565b6000612556836124956001896146a9565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916125b891600401614678565b602060405180830381865af41580156125d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f99190614568565b9350505b50647fffffffff600194851c16930161240b565b505050505050565b336126226117c7565b6001600160a01b03161461264b573360405163118cdaa760e01b81526004016108a5919061401e565b565b80516000908161265e8260146146c6565b6001600160401b0381111561267557612675614172565b6040519080825280601f01601f19166020018201604052801561269f576020820181803683370190505b50905060005b828110156127495760008582815181106126c1576126c1614539565b602002602001015160601b905060008260146126dd91906146c6565b905060005b601481101561273b578281601481106126fd576126fd614539565b1a60f81b8561270c83856145b0565b8151811061271c5761271c614539565b60200101906001600160f81b031916908160001a9053506001016126e2565b5050508060010190506126a5565b5080516020909101209392505050565b8261277757604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b81526004016127b497969594939291906146dd565b602060405180830381865afa1580156127d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f59190614614565b506128038a8585858561300f565b50505050505050505050565b6000612819612b7f565b80546001600160a01b0319168155905061283282613150565b5050565b8154600160281b900464ffffffffff16600080516020614acf833981519152821061286057600080fd5b825464ffffffffff9081169082161061287857600080fd5b61288381600161465b565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b818560010160006128ba8487612ff1565b64ffffffffff16815260208101919091526040016000205560018316156129aa5760006128ec826124956001876146a9565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161294e91600401614678565b602060405180830381865af415801561296b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061298f9190614568565b647fffffffff600195861c16949093509190910190506128a9565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610828565b612a066131ac565b6114d7816131d1565b602060ff82161115612a2057600080fd5b612a31600160ff831681901b614729565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612b7a576000612a6b8260016145b0565b90505b82811015612b71576000846006018381548110612a8d57612a8d614539565b60009182526020822001546006870180546001600160a01b0390921693509084908110612abc57612abc614539565b6000918252602090912001546001600160a01b0390811691508216811015612b675780866006018581548110612af457612af4614539565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612b3857612b38614539565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612a6e565b50600101612a56565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612bc45760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612bed576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612c2491614729565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c919190614568565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0c9190614568565b905060008111612d2f5760405163aeaddff160e01b815260040160405180910390fd5b6000612d3b828461473c565b905060008111612d5e5760405163149fbcfd60e11b815260040160405180910390fd5b80861115611d2b5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612dff57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612f84565b60008087600901600085600081548110612e1b57612e1b614539565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612ea7576000896009016000878481548110612e6857612e68614539565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e9e578092508193505b50600101612e45565b50808610612ebc576000945050505050612f84565b600088600a016000868581548110612ed657612ed6614539565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612f1457612f1461415c565b021790555086848381548110612f2c57612f2c614539565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff1611612f9c57600080fd5b602060ff83161115612fad57600080fd5b8254600160281b900464ffffffffff1680612fcc60ff8516600261486e565b64ffffffffff161015612fde57600080fd5b612fe9848285613203565b949350505050565b60008161300560ff851663ffffffff614888565b612f84919061465b565b8061302d57604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661305657604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e9061309790309046908d908d908d908d908d906004016148af565b600060405180830381865afa1580156130b4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526130dc9190810190614980565b60008b815260106020908152604090912084519497509295509093506131059290860190613d49565b506000888152601160209081526040909120835161312592850190613d49565b506000888152601260209081526040909120825161314592840190613d49565b505050505050505050565b600061315a6129b1565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6131b46132bb565b61264b57604051631afcd79f60e31b815260040160405180910390fd5b6131d96131ac565b6001600160a01b0381166114ce576000604051631e4fbdf760e01b81526004016108a5919061401e565b6000602060ff8316111561321657600080fd5b8264ffffffffff166000036132355761322e826132d5565b9050612f84565b6000613242836001614642565b60ff166001600160401b0381111561325c5761325c614172565b604051908082528060200260200182016040528015613285578160200160208202803683370190505b5090506132948585858461392a565b808360ff16815181106132a9576132a9614539565b60200260200101519150509392505050565b60006132c56129d5565b54600160401b900460ff16919050565b60008160ff166000036132ea57506000919050565b8160ff1660010361331c57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361334e57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361338057507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036133b257507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036133e457507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361341657507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361344857507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361347a57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036134ac57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036134de57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361351057507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361354257507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361357457507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036135a657507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036135d857507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361360a57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361363c57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361366e57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036136a057507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036136d257507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361370457507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361373657507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361376857507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361379a57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036137cc57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036137fe57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361383057507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361386257507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361389457507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036138c657507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f036138f857507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036102b457507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff8316111561393b57600080fd5b60008364ffffffffff161161394f57600080fd5b600061395c6001856146a9565b9050600181166000036139b45784600101600061397a600084612ff1565b64ffffffffff16815260200190815260200160002054826000815181106139a3576139a3614539565b6020026020010181815250506139de565b6139be60006132d5565b826000815181106139d1576139d1614539565b6020026020010181815250505b60005b8360ff168160ff1610156126115760018216600003613ada5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110613a3457613a34614539565b60200260200101518152602001613a4a856132d5565b8152506040518263ffffffff1660e01b8152600401613a699190614678565b602060405180830381865af4158015613a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aaa9190614568565b83613ab6836001614642565b60ff1681518110613ac957613ac9614539565b602002602001018181525050613c94565b6000613ae7826001614642565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613b8c576000876001016000613b40856001613b2f9190614642565b60018864ffffffffff16901c612ff1565b64ffffffffff1681526020019081526020016000205490508085846001613b679190614642565b60ff1681518110613b7a57613b7a614539565b60200260200101818152505050613c92565b6000876001016000613ba58560018861249591906146a9565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613bfd57613bfd614539565b60200260200101518152506040518263ffffffff1660e01b8152600401613c249190614678565b602060405180830381865af4158015613c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c659190614568565b85613c71856001614642565b60ff1681518110613c8457613c84614539565b602002602001018181525050505b505b647fffffffff600192831c1691016139e1565b600183019183908215613d395791602002820160005b83821115613d0757833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613cbd565b8015613d375782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613d07565b505b50613d45929150613d84565b5090565b828054828255906000526020600020908101928215613d39579160200282015b82811115613d39578251825591602001919060010190613d69565b5b80821115613d455760008155600101613d85565b600060208284031215613dab57600080fd5b81356001600160e01b031981168114612f8457600080fd5b6001600160a01b03811681146114d757600080fd5b600060208284031215613dea57600080fd5b8135612f8481613dc3565b600060208284031215613e0757600080fd5b5035919050565b60008083601f840112613e2057600080fd5b5081356001600160401b03811115613e3757600080fd5b602083019150836020828501011115613e4f57600080fd5b9250929050565b60008060008060008060008060a0898b031215613e7257600080fd5b8835975060208901356001600160401b03811115613e8f57600080fd5b613e9b8b828c01613e0e565b9098509650506040890135945060608901356001600160401b03811115613ec157600080fd5b613ecd8b828c01613e0e565b90955093505060808901356001600160401b03811115613eec57600080fd5b613ef88b828c01613e0e565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b82811015613f475781516001600160a01b0316865260209586019590910190600101613f20565b5093949350505050565b600081518084526020840193506020830160005b82811015613f47578151865260209586019590910190600101613f65565b604081526000613f966040830185613f0c565b8281036020840152613fa88185613f51565b95945050505050565b600080600060808486031215613fc657600080fd5b833592506020840135915060808401851015613fe157600080fd5b6040840190509250925092565b6000806040838503121561400157600080fd5b82359150602083013561401381613dc3565b809150509250929050565b6001600160a01b0391909116815260200190565b60008060006060848603121561404757600080fd5b83359250602084013561405981613dc3565b929592945050506040919091013590565b602081526000612f846020830184613f0c565b6060815260006140906060830186613f51565b82810360208401526140a28186613f51565b905082810360408401526140b68185613f51565b9695505050505050565b600080604083850312156140d357600080fd5b82356140de81613dc3565b946020939093013593505050565b600080604083850312156140ff57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6000816141335761413361410e565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b03811182821017156141ab576141ab614172565b60405290565b604051601f8201601f191681016001600160401b03811182821017156141d9576141d9614172565b604052919050565b80516004811061234457600080fd5b600082601f83011261420157600080fd5b604080519081016001600160401b038111828210171561422357614223614172565b806040525080604084018581111561423a57600080fd5b845b8181101561425457805183526020928301920161423c565b509195945050505050565b805161234481613dc3565b805160ff8116811461234457600080fd5b600082601f83011261428c57600080fd5b81516001600160401b038111156142a5576142a5614172565b6142b8601f8201601f19166020016141b1565b8181528460208386010111156142cd57600080fd5b60005b828110156142ec576020818601810151838301820152016142d0565b506000918101602001919091529392505050565b8051801515811461234457600080fd5b60006020828403121561432257600080fd5b81516001600160401b0381111561433857600080fd5b8201610200818503121561434b57600080fd5b614353614188565b81518152614363602083016141e1565b60208201526040828101519082015261437f85606084016141f0565b606082015260a0820151608082015261439a60c0830161425f565b60a08201526143ab60e0830161426a565b60c08201526101008201516001600160401b038111156143ca57600080fd5b6143d68682850161427b565b60e0830152506143e9610120830161425f565b6101008201526143fc610140830161425f565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561443357600080fd5b61443f8682850161427b565b610180830152506144536101c0830161425f565b6101a08201526144666101e08301614300565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b82811015613f475781546001600160a01b031686526020909501946001918201910161449b565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6080815260006144fe6080830189614482565b828103602084015261451181888a6144c2565b9050856040840152828103606084015261452c8185876144c2565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016145615761456161410e565b5060010190565b60006020828403121561457a57600080fd5b5051919050565b803563ffffffff8116811461234457600080fd5b6000602082840312156145a757600080fd5b612f8482614581565b808201808211156108285761082861410e565b84815260a08101602082018560005b60028110156145ff5763ffffffff6145e983614581565b16835260209283019291909101906001016145d2565b50505060608201939093526080015292915050565b60006020828403121561462657600080fd5b612f8482614300565b604081526000613f966040830185614482565b60ff81811683821601908111156108285761082861410e565b64ffffffffff81811683821601908111156108285761082861410e565b60408101818360005b60028110156146a0578151835260209283019290910190600101614681565b50505092915050565b64ffffffffff82811682821603908111156108285761082861410e565b80820281158282048414176108285761082861410e565b87815286602082015260c0604082015260006146fc60c0830188613f0c565b86606084015285608084015282810360a084015261471b8185876144c2565b9a9950505050505050505050565b818103818111156108285761082861410e565b60008261475957634e487b7160e01b600052601260045260246000fd5b500490565b6001815b60018411156116745780850481111561477d5761477d61410e565b600184161561478b57908102905b60019390931c928002614762565b6000826147a857506001610828565b816147b557506000610828565b81600181146147cb57600281146147d557614807565b6001915050610828565b60ff8411156147e6576147e661410e565b6001841b915064ffffffffff8211156148015761480161410e565b50610828565b5060208310610133831016604e8410600b841016171561483f575081810a64ffffffffff81111561483a5761483a61410e565b610828565b61484f64ffffffffff848461475e565b8064ffffffffff048211156148665761486661410e565b029392505050565b6000612f8464ffffffffff841664ffffffffff8416614799565b64ffffffffff81811683821602908116908181146148a8576148a861410e565b5092915050565b60018060a01b038816815286602082015285604082015260a0606082015260006148dd60a0830186886144c2565b828103608084015261471b8185876144c2565b60006001600160401b0382111561490957614909614172565b5060051b60200190565b600082601f83011261492457600080fd5b8151614937614932826148f0565b6141b1565b8082825260208201915060208360051b86010192508583111561495957600080fd5b602085015b8381101561497657805183526020928301920161495e565b5095945050505050565b60008060006060848603121561499557600080fd5b83516001600160401b038111156149ab57600080fd5b8401601f810186136149bc57600080fd5b80516149ca614932826148f0565b8082825260208201915060208360051b8501019250888311156149ec57600080fd5b6020840193505b82841015614a0e5783518252602093840193909101906149f3565b6020880151909650925050506001600160401b03811115614a2e57600080fd5b614a3a86828701614913565b604086015190935090506001600160401b03811115614a5857600080fd5b614a6486828701614913565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1bdd568bd039595fd4624b89bd697f998c689807d04c17303be9d1417adbdc056f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 9492 + "start": 9647 }, { "length": 20, - "start": 9680 + "start": 9835 }, { "length": 20, - "start": 10598 + "start": 10753 }, { "length": 20, - "start": 14906 + "start": 15061 }, { "length": 20, - "start": 15357 + "start": 15512 } ] } @@ -1760,28 +1812,28 @@ "PoseidonT3": [ { "length": 20, - "start": 9275 + "start": 9430 }, { "length": 20, - "start": 9463 + "start": 9618 }, { "length": 20, - "start": 10381 + "start": 10536 }, { "length": 20, - "start": 14689 + "start": 14844 }, { "length": 20, - "start": 15140 + "start": 15295 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-b4c3d00ecb469b7feb2a44356615e2767397e101" + "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json index b14557440..a085967ec 100644 --- a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json +++ b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json @@ -1486,5 +1486,5 @@ ] }, "inputSourceName": "project/contracts/token/EnclaveTicketToken.sol", - "buildInfoId": "solc-0_8_28-a76bd0705c32dbab3093dfaf9992a5e145a8528d" + "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index c88df390d..1e5352dd1 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -198,6 +198,10 @@ interface ICiphernodeRegistry { /// @param sortitionSubmissionWindow The submission window for the E3 sortition in seconds. event SortitionSubmissionWindowSet(uint256 sortitionSubmissionWindow); + /// @notice Emitted whenever the registry-wide accusation vote validity window changes. + /// @param accusationVoteValidity New validity window, in seconds. + event AccusationVoteValiditySet(uint256 accusationVoteValidity); + //////////////////////////////////////////////////////////// // // // Errors // diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 37c7491bc..98f8fcb79 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -135,6 +135,26 @@ contract CiphernodeRegistryOwnable is address public pendingDkgFoldAttestationVerifier; uint256 public pendingDkgFoldAttestationVerifierAt; + /// @notice Registry-wide validity window (seconds) accusers stamp on accusation + /// vote signatures. Ciphernodes fetch this on startup and add it to the + /// current wall-clock when populating `AccusationVote.deadline`. The + /// on-chain `SlashingManager._verifyAttestationEvidence` then enforces + /// `block.timestamp <= deadline`, so this value bounds how long a leaked + /// vote signature stays replayable. + /// + /// @dev Set with [`setAccusationVoteValidity`] by `owner()`. Defaults to the + /// [`DEFAULT_ACCUSATION_VOTE_VALIDITY`] constant on initialize so newly- + /// deployed registries are operational without an extra setter call. + /// Setting to zero disables the off-chain freshness window (deadlines + /// collapse to "now", effectively rejecting every vote on chain) — intentionally + /// allowed so governance can hard-stop slashing in an emergency. + uint256 public accusationVoteValidity; + + /// @notice Default value for `accusationVoteValidity` applied at `initialize`. + /// @dev 30 minutes covers gossip latency, vote-collection timeout, and mempool + /// congestion while keeping stolen signatures from being replayed indefinitely. + uint256 public constant DEFAULT_ACCUSATION_VOTE_VALIDITY = 30 minutes; + /// @notice DKG anchor commitments stored when the committee public key is published. mapping(uint256 e3Id => uint256[] partyIds) internal dkgPartyIds; mapping(uint256 e3Id => bytes32[] skAggCommits) internal dkgSkAggCommits; @@ -199,6 +219,11 @@ contract CiphernodeRegistryOwnable is __Ownable_init(msg.sender); ciphernodes._init(TREE_DEPTH); setSortitionSubmissionWindow(_submissionWindow); + // Seed the off-chain freshness window with a sensible default so new + // deployments don't immediately need a governance call before slashing + // becomes operational. + accusationVoteValidity = DEFAULT_ACCUSATION_VOTE_VALIDITY; + emit AccusationVoteValiditySet(DEFAULT_ACCUSATION_VOTE_VALIDITY); if (_owner != owner()) _transferOwnership(_owner); } @@ -361,6 +386,22 @@ contract CiphernodeRegistryOwnable is /// @dev First-time set is also subject to the timelock — operators must wait /// the same window before the verifier is active. For the deploy-time /// initial set, see `setInitialDkgFoldAttestationVerifier`. + /// + /// @dev **Node-operator requirement.** Ciphernodes fetch + /// `dkgFoldAttestationVerifier()` from this registry **once at process + /// startup** and use the returned address as the EIP-712 `verifyingContract` + /// for every fold attestation they sign during the process lifetime. + /// After a successful `commitDkgFoldAttestationVerifier`, signatures + /// produced by long-running nodes will be rejected on-chain by the new + /// verifier (different `verifyingContract` → different EIP-712 domain + /// separator → `ECDSA.recover` returns the wrong address). + /// + /// Operators MUST restart all ciphernodes within `DKG_FOLD_VERIFIER_TIMELOCK` + /// after this function is called — the 2-day window is sized to give + /// operators time to coordinate a rolling restart before the swap + /// becomes effective. Nodes that miss the window will silently produce + /// invalid fold attestations and be treated as dishonest by aggregators + /// until they restart. function proposeDkgFoldAttestationVerifier( IDkgFoldAttestationVerifier verifier ) external onlyOwner { @@ -625,6 +666,23 @@ contract CiphernodeRegistryOwnable is emit SortitionSubmissionWindowSet(_sortitionSubmissionWindow); } + /// @notice Update the registry-wide vote validity window used by accusers + /// when stamping `AccusationVote.deadline`. + /// @dev Ciphernodes fetch this once at startup. After a change, in-flight + /// ciphernode processes continue to use the previous value until + /// restarted — operators should coordinate a restart if the new + /// window is materially shorter than the old one, otherwise stale + /// nodes will produce votes the on-chain verifier rejects. + /// @param _accusationVoteValidity New validity window in seconds. + /// Zero is allowed and intentionally disables slashing submission + /// until governance restores a nonzero value. + function setAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external onlyOwner { + accusationVoteValidity = _accusationVoteValidity; + emit AccusationVoteValiditySet(_accusationVoteValidity); + } + //////////////////////////////////////////////////////////// // // // Get Functions // diff --git a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol index a0f0ccf67..bfb8508fa 100644 --- a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol +++ b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol @@ -143,17 +143,28 @@ contract SlashingManager is ); /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. + /// Exposed for off-chain signers that recompute the domain separator manually + /// (e.g. `AccusationManager::vote_domain_separator` in the Rust prover crate). bytes32 public constant EIP712_DOMAIN_TYPEHASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); - /// @dev `keccak256("EnclaveSlashingManager")`. + /// @dev EIP-712 domain `name`. Must match the literal passed to `EIP712(...)` + /// in the constructor below; off-chain signers MUST hash this exact byte + /// string for `recover` to match. + string public constant EIP712_DOMAIN_NAME = "EnclaveSlashing"; + + /// @dev EIP-712 domain `version`. Same alignment rule as `EIP712_DOMAIN_NAME`. + string public constant EIP712_DOMAIN_VERSION = "1"; + + /// @dev `keccak256(bytes(EIP712_DOMAIN_NAME))`. bytes32 public constant DOMAIN_NAME_HASH = - keccak256(bytes("EnclaveSlashingManager")); + keccak256(bytes(EIP712_DOMAIN_NAME)); - /// @dev `keccak256("1")`. - bytes32 public constant DOMAIN_VERSION_HASH = keccak256(bytes("1")); + /// @dev `keccak256(bytes(EIP712_DOMAIN_VERSION))`. + bytes32 public constant DOMAIN_VERSION_HASH = + keccak256(bytes(EIP712_DOMAIN_VERSION)); // ====================== // Modifiers @@ -192,7 +203,7 @@ contract SlashingManager is address admin ) AccessControlDefaultAdminRules(initialDelay, admin) - EIP712("EnclaveSlashing", "1") + EIP712(EIP712_DOMAIN_NAME, EIP712_DOMAIN_VERSION) { require(admin != address(0), ZeroAddress()); _grantRole(GOVERNANCE_ROLE, admin); diff --git a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts index 31608822c..89cb00338 100644 --- a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts @@ -87,6 +87,17 @@ async function main() { } const { ethers } = await network.connect(); + const [benchmarkSigner] = await ethers.getSigners(); + // e3Id / committeeRoot / sortedNodes are forward-compat params; wrappers do not + // bind them yet (see BfvPkVerifier / BfvDecryptionVerifier). Any fixed values + // yield representative verify gas for the Honk + wrapper path. + const benchmarkE3Id = 1n; + const benchmarkCommitteeRoot = BigInt( + ethers.id("benchmark-gas-committee-root"), + ); + const benchmarkSortedNodes = [benchmarkSigner.address]; + const benchmarkCiphertextHash = ethers.ZeroHash; + const benchmarkCommitteePublicKey = ethers.ZeroHash; let dkgProofHex: string | undefined; let dkgPublicHex: string | undefined; @@ -235,6 +246,9 @@ async function main() { dkgPublicInputs[DKG_COMMITTEE_HASH_IDX.lo], ); const dkgOk = await bfvPk.verify.staticCall( + benchmarkE3Id, + benchmarkCommitteeRoot, + benchmarkSortedNodes, pkCommitment, dkgCommitteeHash, dkgEncodedProof, @@ -245,6 +259,9 @@ async function main() { ); } const dkgGas = await bfvPk.verify.estimateGas( + benchmarkE3Id, + benchmarkCommitteeRoot, + benchmarkSortedNodes, pkCommitment, dkgCommitteeHash, dkgEncodedProof, @@ -275,6 +292,11 @@ async function main() { decPublicInputs[DEC_COMMITTEE_HASH_IDX.lo], ); const decOk = await bfvDec.verify.staticCall( + benchmarkE3Id, + benchmarkCommitteeRoot, + benchmarkSortedNodes, + benchmarkCiphertextHash, + benchmarkCommitteePublicKey, plaintextHash, decCommitteeHash, decEncodedProof, @@ -285,6 +307,11 @@ async function main() { ); } const decGas = await bfvDec.verify.estimateGas( + benchmarkE3Id, + benchmarkCommitteeRoot, + benchmarkSortedNodes, + benchmarkCiphertextHash, + benchmarkCommitteePublicKey, plaintextHash, decCommitteeHash, decEncodedProof, diff --git a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json index 23b688cd1..d6d7a2087 100644 --- a/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json +++ b/packages/enclave-contracts/test/fixtures/bfv_vk_binding/folded_artifacts.json @@ -1,10 +1,10 @@ { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000f53dbb3615cad8357000000000000000000000000000000000000000000000009a1d34b7e2d8c2c2300000000000000000000000000000000000000000000000e3c15f1d9a07c7acf0000000000000000000000000000000000000000000000000001f8f7472abfc300000000000000000000000000000000000000000000000aa2049ed9ff1229bf0000000000000000000000000000000000000000000000063cbacd299c0eb9a600000000000000000000000000000000000000000000000370dd16b6afb381350000000000000000000000000000000000000000000000000000a4e5b3a4081e00000000000000000000000000000000000000000000000a21338638546b6e87000000000000000000000000000000000000000000000005f3cc20e5b7b11c6400000000000000000000000000000000000000000000000bc860a8f566b2782b000000000000000000000000000000000000000000000000000185152431351d00000000000000000000000000000000000000000000000c51f8b01dabc8adeb000000000000000000000000000000000000000000000007e4929e969d92cec900000000000000000000000000000000000000000000000ab999373bb5814f010000000000000000000000000000000000000000000000000001b40ef30bf3c90451c5bb2caf164d374b7c96c358e5baef15971e99b5260f38630791d74696bb23220fb9443bdef2a7491c300618a5f75506aa6391df2fc8ed4e2d347c1ad4b90f9748a44b88ec4f79b09d7406485355f3c44403899f531a0b2efe309986fdee0f0129a31d8d123d6e44f569ed814f142cfa5309256218f5ade73d5caa0038961ac66ae3b121f738a38109c13e385c269b0387bfa24b993409c01470e1eb63752f4d9cf747b975097a06c7ab48f11ccca1b0848cc6d75a60756bbecda66390e81588fea840ebaada47351091f07fd53a0661cd6c76b98cdd061ff46270959537099a6272f30de4490d132fe4edadc9f7f63c865c1265faf7c531764653cc60c307af7f4ef05bc70dc756c806372167779f52e94725f5d5125b87a230aac388822cb72616e045a2f20ef10a251550dc7eace0ea8cc0604c9c4a624773daafb28c22548630273a92e339c80321122881fcedce0be138ca2a2453b4d94e27468b0622cb8b550badbffd877a3b93870669caa8eac7d39a5d35187b212e46249551ee1730e804feaa9e847299184ded492f4e39e5e06f736c084b9597a5877eeef8a905eed894db2b6a935fcfd8bee52c90e5c405683cf4d9f9c982ea99ff6b67fd882afefc31fb2e9ee8d8c69e5f09721cb766c50c5f423e817ad74c752aa61d353e19835a0ba7bd6056d2747808cc8e621c3df08fd2a3cc17ed5182452b2f3594f11bf0011a890c5bbbd1052bdce9c18651d153b1844e11a223775f54554057f4c716e3b3bac81dd1c1774fc0771916646d934753eb63f6d65a8c9d9c304ece2864250efaef1edb76fce82029ca710eee55d492f0c6f789ed0b07d1f0b810f0bbde0c26d8be4e8584b10ef4a171cc09a0f3269668ddd8a740cabe08d77b31d8e4a70d0f388962112b68f542184b139ed278a41b4c7d7ab998cc9b82d2465d0e315430286d6a15ef18c7581365648f24f3fa4ae77f021e320939e13a5899bd785404112a639d3332f81c037b404324e6e1c93c8976d49ec69eb406e4ee7dce59ff8908aa416ce08e224e059c1a6380d4ffc075e74c6c45281fda49db67a94cef19501270dedc9e1a490355b61eadc03d55ae1a9e2cae2f990e7fe5c3c6ef5669a29007b7b65d0fb59aed9e71613738a1be1f4f5a3ad7858307e95730592734ebda4d2ea73936c515af04d5e6a4316c20c86c6203091399a3005b3c40f57894e0d5d80f0cb4b2858117cc94383c6bccd0be969b5e517f8a3631702145dad752591c590b61a8e3f833a80a0dc69a0631714977ef66d1d29bdd09368477939fa03b18f51e49b2a522478dd3b414ccc898864a55768449fe95cf07d08014ea2c5d236c3d0d423a0a9b724ac5d5644104b265dfb9cb078bcb22ccf44b471efa2071b5efad14c3c88e764ec75ae5ecd4755b8a20d6eb6a25e6e9d37f5f76b7fd0d4c6654c51344a8912711a3577ed6d8aaa654522776d1f12e4dca25657a2235377e65c87b2f53fe531de60ab8c4e3592a0d36586308e1437041cd07327dce0fcef14f050524d3fde7e53cfc434af9a8c646c8e9e231bd8286c3803c89cea564a9cd0f022b2367c7f6afaaea30b89d8b44dacca4e6e98da34fe8d97ca7315fc5bb704282d81d99e608994e0003d7d21b0e60e4db83bc1272630c2e8fe23a8a64ec6fddae7c1d72f7667c3314bdd208a791900d672ff419770912924486834068d7205a229f1f250f56bbc23667154e10c1fecdd4bdb0708546a5b62ccdf1195dd0acee346c1fc460a412d44e0835c25cded8b5643d0585483df4b03afee6bf2b0be8167bbe11aa158a875999a3aa2ef9ed382a30be7bb9566c1c4bca9961483478fba6f0d92540de51ea5f67a18d7a07625754e8a545662bc73268373d69c61dfaf2dc53b508970e01fd5b346c57f10920822131d075216015a7f3e89d66bc71cf5dac8e4b00b83c12602f99d788d626d330061426184da2ed4056bce28e306949e76fcee71a4301b32d1f67ed77452939da2e285b46e13266582823bab2f1c3035f2904330df195c043046cf89571ece49a6cabbf9e943933fc1647058e09d05635230e53107787d4608fff7804d0ff02f3de3a66ab22d381c7dfb51478d84887fb55378e1beca8a3d4cbfcda3b098fb66d7ce4483390e722e9a936e542e557ca29a54b2e281aefbe586ebfb38fe5f68d8ca178a51902b63f9128665dc84bb3e86cb5788722ce35a7aba3ae887525cd32363849be1430db7013532eb4400ed55d0e71c8a01002152e48987a0a5a35653a6ba485afa079caa2a520ed9f72586ae8d130f9081da06b7e167b359623d5f1b12e6a9bf18a5f4d1aca33e9bd3e4fb266c360d97b002e5c13ae200ad7ca60e9677f73eb79a98b2306ebbb433f20f148ceff4e06610174c9bd1dc7b0030d4ea474c642f9fdd9a909d133a65dc672e4f069ff2da59215566da4fa08a76e3e2a9634fe5790e0500dff88ab6c1a3f047070202223e36f05019b3869f887544fc400bb3b51388c62a96103bde7b67934c0ed0db9bc3ee72716bb9f0271550deb848bc74a28a1aee544acdab6e3472ac25eb679493074e7014987fc4f8b92c0220ef132070a3925fb0f734727388762022ca679fd9121cf23a27601d38d23c2e91a1a9e5d4860f4c93579b0982503e58792dc9ace29455126474df651589a91bdc5b80ea645e7c44f6c3c05403e34ced369acf3a8ce7d4f1f576103a6d13f8b15b3b2ec4382ef7d0370ef3c5db541d4d54c1ebd5632fdb4142b3f697ac0e94cb884cd5e45453e55b52d552c9330055e71eaf6fd613ad8611f7cf75022c65538928a20f760aef97960fa36243b5db8a91c61cf26def5da31137ece1dacf10661ac08a12d484b143e6f45752d14d7acc904bc5a094a6fb3081a05d242fb83d6f84e91e965381bbc9b11bd55a0ea8ea6fab6c8e289a0f147bd1e729ae0e3e422496cf220b55b394cd4a92fbe43c5034d44e7473f3559371de1050d4e1b0cbd01023d18f5087a619a78cf0416cd6e2e5dde211cdcb0e834c9b9059fba6838662bed24a4e9f37a7583a2a72de506e384e5c70c465a1fcd4cc42800548b9bada85fc3596fbfff9a508fcd42bc0ac8fc3972e86bb8f2376f9495ff29b1d10f89705da570c744b77cd3d7321a122d304ab4ffa6d2c6ed9c760c4b6d0fe0ae339a75734f57cfca627ebea65eb03edbce506a3f1722fa8d238b4db0371a6327eee892cc246d4974cf9b78cec65bc38f4835c1f5c4aef02b2b54e71ee405cf45d31a3432cd7afc100dfd50ecc0894b19600ded8ac6e02756f725d0f19914f7478e44afa64e7d00fe992f1d4f59872fa1b8ef0115e69b1044429c4729c117102d5b3fac786444a171d7f6c80ac2f9f5ec568bcab3c632921dddb5fda2410dcabf653166ee9224d41aa0eb9ccda051f2c800d792803088eb8a707b3112342b4a5a5ad021bdcb2408a88cf4277954944f174e04aee4562dd8a18ae6c119e5253df10afd23922247552cc74a55c13fb5d73249504a98aae929d2e3aa3320991746baf937f20dcc086281a71feea77f316d06bb62ff13ddd03d70c82c298c212d7365134d76b3a36d9325f834dfbb84dc4978dadbbee821fd814eb842451f9507f7de25ecd150b54acb7ee749e7b6cd2014c41bd6fa31c468376955aa6ce1771f21a381c32ed7c504b42b7ad5e181d8cb1cd3f41b09398460730076bdcb33ad093868935a0ea6953389948cc9fb8526dba609fbf4d184697c29b4acfd01c6ca2ede99c6628f0d99ea7ddcc2ee8c3d3284fe22d4cf23c2e92a171a5d5cb4188026e15e0adc191ed2b0ce1e0a5602e881eb6c1c0f65c125e20940f59ae5836fad1e44a41ec4abc6573d7bd0002816c5fe88a49e05cf81803abf305229f613d645007c8a867254edd13ee7afc09373639b0096166e232a9093ef8f0f96f8fff7950c81f2b547b48db6bd2d98d66d125c78a5981c959fa0a7bbc845204cf596506a2b1de12f3aa527da68e14933275f799b0b50c6f75bd4822c429067d92ad2cdd61fbf4cc7fd4e75259d33ee7b31aebceccfe19a0d56797a53d8918a8861680bd704fb6ea78cd2eed94ac1f7ad34746bf4e62867fa8e77dd6ffb5ad2b0ae1f61071ab9e852507202a97ba7c8944acbc01d1d352a8f1dbcff719df3bb7dfa6636ab1295a26fd3ffc8044b710870234c8ef04f5dc0d709fce3554d1043b3ecde5c7c1c177c30f249e44f54cba171e7957e1c3c039a14586873a8f84e1eac7bc6e87c20cb54fd0be3f9b64310707b053917cc4274a90ef0ae504b64f6589ba9bea46525bbcb27ca392e2123f88fc5ba0ac269a0caacbc290529edc2d9e4c01417c27c06dcb31575584c7d839d9411e50c81158faaf2fac899cd4a4ef948b99c64ef36113e2ec7327372d2c48d6fe310ceb9f4815ff144fdd2baa33c8df8157f2c8f17229e9ff8cf302b36ce3a9f5da4ec3f2f9ec74f76fe55765680d70e24485c202803117d860bd785da849d43a0db09f7577ba095bd4c02519ee6941face8d616432010ddc416bf55cdbd4a6e9e6efd993b0d122ef34daa15f86e93dde4577e287811fcc7c2db977428feadd96537ba4010e521ce4e4ba75c0943d270cecbbc045b1d6e9db72bc59d25dda583d413f886976efae9075987e1f5078933f5b0a619e21c855a147257d70b7f9657cfabe2681b1864abccbd9d69653a6b4342db67af2315ac265b2d627b21de5f070e0f9737313e7792cc7ecb8fc9fa35b98a1f8b56541351d9bdfd22101887c6debd219a39c786fab7c13233e3c8c742e62f792377e91e2fa5ac896c489867c3dfd4a954619964d5a4dd306e7e5242974c29c7ea34ce0514b1c7a04665cafe79acc47db2325fe52d1b978ac531afb8439b9b5ee8acea11d8630ae5a7c486f1df468ceca9ca87f30df23cf6d3565e17f9fda6a1b1a06829cfa4d470d4433e2e6d298d99e6c49aec3ad1fffcfee1966d67e886d14365811109d205318b09a7c2309cb7d099c2dd0e4326c5812d5c966bf5209cdebff0a01d79ad7bf8847b41973f0058dcdfa4a0ea8105b254c04e1fedfbf588601745b52cfb9f6efaecfc1fc5f07816980a450a1eeeb34244e6319ddd631497dffe58360b59309eb15f63abb0d0e6c099407a0655e816f269c2c8b79dc3c87b7e18219d1ec346fdfbfcdccb0a403e7b176fd0609c36a3f46ce3df07a873f475ca97dd35302610de77bc0973b3c2370c3464753b722aa5fb9d760276dffab2a757c355602e3e018f31470db36ce0351a1cc0823f6321c70a0b1197f56e54d0e9decb0fee29da67b8ec7c485c4e35ef5edaeca1f8c5ff12ca57f904f737de97fcb72b81122566ed4226ffb8ae70b5fd984784564e2063b0a67c7fc04f487f8b73a3236aa31afe58c1718afad7a68d28d680ac1feb069d008f5e13a4e338ead29a7ec333632cd3cb379167d23bdc6147b2aa9fd8d3025867be3dc8af5ce3ad576421bad8fa091591f988e3cde4f684894e57d1d8c5a08ae11bfbd79d03734abe1e40cc1d1a236c94d0b200de14087d54ea627a4aff7f21ec30bf0886f4ddf0d404f9cd322c11c4bc3db97075b7a7fd10aac219b19cd12d5dc1eef506fd93ae9fc4494ccd2f160b162901ff434af8224f789e42307741f394447d2d24e1c61eff6b26b2238c29164909260ed975641bd83a4a5a5e5a3a0d1438a63fa284aefaf826bc11d12c10ee9eea904a50f33047e55a82b435613c5d7f5a63a2c202e8b6a5a161f2930912d3b9bcda4b856a658640ba988fa7f64c4c242e3d2cbc44ce892567be98aac917b423ba0b9b6eeb97ab9db16a9b2a2558f4a6dbaa63d847387114020b7ac22104463ceedeac9131a14448e0079fefdd8d3fa1064750308bb21b800b3b9b88060d54d8872bb9e6656f261269605504b6828ad43565e5c92e0f451b3d67b5b0f40087306a467a3f9911f01c5fff23391874cd52c9edf3d95999b9ceca945ad90718b3d476d9a979d504ab1d3eee92b16964bac2833756b9ad845a6cb8df462b2525b10643b72b7a17d9b2e7c082c2e368af41e8bde57b4266f3d4a0e632eea2ad0e66242db844dda07c5fa18d4c9403263fe34f033d057c9771282126966d312b26ff47309369bc3bd2e0b8d327bac8e1b7c3e9cd2d64e723d33869416b4a1ea80b180740c06bd7521f8c77f5798affb5a1d4e6aa2f2b99834c08e78f9d1d9a820a951876e47f6cd9506a43f3c9bd27fc4422dee8ace5747bc95551c4e132f43d0e44a1fd8f56263a6316dbea89f7b3831f4840ba89db2e72fa695c81f8fadeb32593758659a927e047060ea750ead71a3b1b4321336299bbe6702f94970f6aa817f6c0ebfeee024701c9453c5521563fc9185b39e503bdf7e7a0542d8f7c60012e0ac769cc449cc2b913696ee7d4b98cea6ed65497003679bc6651807189276e28c5ee15204c9a7af665acd087092c030f76f5776a692dd730ef82f857eadc2f0e6b83cf868207ab7b2a2980c3e4d093913f57220d74b735958fddc92adc2f2e157b5e15f3f87cfb67fe1d2e0f684538e1aaaeb3b654b260dd11ad5838dcee0723f8cf1a2f629f62951d7be72b24f937d6e608387d1ca69c741c8fbb8acc3c97073c33c17d50c487e25558c86e0bc8b496c4c30269f4766480a2cdc28aea39ff228e20512c24f9c109661e936b3d42093c81d5ef0227ba6271af8bdad0412842034f6c6fd4cc7f63a79afeef71b40b9e26a6e1f6da2618e1e0335e6aa0ff84001987cb3e414a95a4813270bab48f897f38feea011b2168f0fab126fb081d24320d548a17cbf26696f9402b88592bfaf81502d38c0a595c3fa3f2069545729e642d2f98492854db814e58b533f6db719bc6163aed2524ffdd42c3a48166c5ba7f21a580c689d59db27b30e722fb1f118963c97c14b40ad4d7fa03053eed87ec670061f2f4824d8bed8a4fe6401d35372d3b42bbdb6ff13e546577d8b38645518e1ec0ce4a9d670b6d4bcd01635c8328a89cd19ae391b3cf2ddf692a06927c378c149d8de4d7f0199aeca659442de514851b4f98af06c897b6ac235f7d0a754a3015b728c70c1c7064c98e1e9b97c617b0b2a4d6ef6a70357825764ade1fd9460714dcaf36c3db239ec639ed154431c80bf4df9d6c6e3065d966a8c8240fdf2209230dfcd13621b17acfe853445db711fe9c7727eccacce0bd66a732d0f2330bc829320961f7e64285f0c83e62236c14f4c43ae25be3100e7a68ea5d2c06aacd752b5c965142b312acaa3b5127c5fc3e4f67d273d2411542082ff83dfb6415306b21367cef5c4ebed8e8ff742691a461ed9398bba83e65ca8d09189dff137664f22967b1445325643c0167c81f10f27226b608ab54d45f8ea5b2680b0ba2ea657d26016ccb38e502f41cb4b29de0540dd0e8e77c082fbcb41ca1af63a74fe438eb0349344d937ca9b1c0aa5e230f5dce035511c9a78d603f7eb7a1988bce41beae006995d37a978272226ee69dbbeb858f7c7be00c1679f9ce69b598635a14b87b28d8c8024e7a8b502232719c781dce4ac0b2faadce0f653bf0282dafec92fd6c0ee7b3aeb390f19b6e9a674c2bca397b89d00b5f2380da7665e3d27bd0f0319b28416709dc0077be3e7c3a13ed36f0c011770f660532922401134739a8b3ddd401373ac2ccd729c0ee3ecde423ce895da703fbfb79ae9b8d73988f01c5bb668f04071d0857ee1685e8324dc271eba7d82b3cf17d1061096319e17b74a2676dbe0a993ba468e54b0885aade45d6ebeb9f3aa0b01cbe8a9e07fdd509ef2cbde46724dc6527dd24149b01013ca22ce9b5c6ba415312f6fd721c7893616678589dc620511d9ac88f407670dac9e32ce69aa3b381f6a248f62f929847e79d91c3636f115a5a060b501f3d8e94971a4423e3047b674ca523d5eeca750c6ac4040ab7700ea8bf869ac033be4a0844bb977aa3a15059c14a73a8478b4866fbf37a7d95da0c0c58b31a69405fcef02c6d03910da1a6652185146864b8c3cdf2f9b333d3701e7d77145a1bfe3f47a793d09f5ae07ad54149a153779d57e5ff1f99b3c801c1265f7f8139255eed555266cf54575a5898b0e59fc407dc065abe7f3f9e6bbe0a129d4480b5f5398e892263904d29e0cf4e0173ebd46e32a826651430d45322f8236f06fcc0575c6f5b38d5aa4152a4aff211a8a27ba79e848113eff340a333c80488903e5e9d166f326984f829d83a3f504c5e15e067fd532df02211357215421d342b58105f5a70838c9b6e94c2abba828d10ed9a8f1ddfc17d8ada7ade7b82169ad12d4ae9c835842717f6e6711f9f02cada3693f16d52b2176c167299ad610060de5d572dbeddbf940c771912c02c0092a7b3aa5fa1d9332acf019e94825a00da0fe94a891fd2ce503929d0c79790cca07dc405a1e33e731bc9acf3e815cc2ad39e40581c0302665a25ca94c4a12312de7ee6148d4cf8ae1291ffec8171f4297e51b0dfba8961199643cd931b56aae9677491ab3b8297d03fcd3597886ebe256c3123d478267267ec7b52bc117db3fcd7d03f7e94c9c7ec6455f70630d9e90582cf09ac6f274a845ccb88e53ab0f58b7a18eb9589357ee90ce97721395ef72e0c090b9db7373e5b80262a17b68261997b8a6c766c07a6c1a862e58c2b253b2a55876709d976ac1affb939607825bf06fd894d560a035b248879b93d413009146d6b38979107825a8c6a53a09522d2ddfb2ce09f1109fac35e6b981081df911696e41bb39bbfdb303e48f43bafe21e9ff664c46939b9fea7b24c6504fc520521b7b76b9c695305c5ba39c155cd85d4fd40097e8bd455b70b2381768988f70d22532a1e4493c0f8dac78758d97d275ee8a456c15870c8bb744705d9d22e37280cc7370b33030aeffabc2ff96875e7cbb8ddeaaa53337cbf484a0b55807868f60aca9963498523deb0b723d53780d05d0dc540420754dde76bc983cf1bad3f640babfdf96abd22f2037cc07cfd636f8ecd95a7d9cd538d5369eaef37bb927ee824a4cefec648edb53e0b6f15cd92489cdbd73e3f3593707b9870bc02177bfc9a2ecb20a3db33b8a701adbcbb763bb7995efc43360f610e8406cb6cd1d4b3e2b5102476bd8409aca30fbd844a62037ee5aa4a9d20c4e5d363de9ba119dad5b3b60fa2f88ba958610bf78e45598812fca72a09911640f9b0bda2a7f6f3da6e328f15043b12888ff80014a1fd51ceefd7fca6aa697be3a99d74f8cda49c0c1fb9b805bd285a7d74ad650bdfc3bcc2526abb27d09e21df14bd631ddf59031153edb7199d12bf54106d8682279dae062d4441a3d67c37f54a0f1f44bbc0e30c58f232198a9f018dd1ef7d850f4db9980e637b5d24d4f5d10884d5ff5862f37c6c8f5727954eae978b8c77b9977e516d335b0756cd617c9f262845fdfeb60daa6edef61e7f0981720852a7966495d9e706ebe75441024d291daab17d54bc9a241f591d045d5d8251e04b691a48ac17ee74c4176a689aac2621a58183b16639f1953438113ff5971a863fa09866f5c03031d619f100655e39956bc3517109ff56bc048f05a26b0f44bc29f05c558cd1a7c91e340dab26a6c344cf54a3448f1e8c7d7ced22cff67a99b1e6dedb28a84409f85ee8ad2b88836d27aa6a1430e256698552031d1de3f3cbc4052cd07950d998edd5431c8bbd950e95a651197b4e7eec0e118a0a73282d5da28fd57114dca6fce3e73525b3181ce299691ec379dfb7fb0e27e21be0f37a239a5c11f3fa764ea5231af506f8d1f52d44473918a8e9774da0fc7d26fa51dc713cd9db1460b430ed0ff382fb3c32d48f8b427573ca90c131f392a42b6554465d6c7329fe34a7fc40c1495302869d0b71ca67425aac8ee43513e6f525ae9575de58bd817af0bef9116e4349bc544b15376f2e9e103ba8509f11394f1f1cf7538075807939d2e39f20e60f1ed94b52b04d23b6c9100ccaeb56bf57b221041d2b629e7ce445f2eb4302e38adfcfb5b1345e06ad287cec4171028edfb410b0dc68a182b48afe396998bb2d9f3430ecae30e37024f105c0a0c064dd9e1c21e96994477e4557aff3b21c3023360ad319c9032f9c3280d916702a6dca9f841a20a1e753cb3a1d03658f0003faf218bf2e7d75f7f33818241f7a5db49a7de00f9e6e7cdab05f746256a9cd5f230eed544cc23b951a2d061bbe19226a8553c608b9805bcd12621d2e902e92b3efe99a8563cb8dece0fa5507c59f053e1b100a201aceb581b40cbf5d15ddb9f3b677b99e46cbf717a4765027bc1a64ef4d3bda1113575c24942fc87eb3e1ae41a27e2a02a79731b4e98f3cca0b60dbda8eb7dd19fa6aa3096ffe6737ef94ad23e123445b015387bb2533ea2c1d679404aed16013b5df890b6ea8a96222c9af9d35ff0883c62dad505b44c87ceea7b4263a1b9d20e68896fa9dc42e6e78bc9d109ea731120426e1d51cbae23d6cf2b5fb97d46c0b8474c898ab887fa0875b9a22cde667c7c8c16a17fedde7e3dc653863a356fe090b73fade87982fb63b1c9ec6ac1ecadb5db95b916ed11d375a0bef0f3ae0cb18ee143f9eb74b148d264073e9230ab50134026c1677f9b830a1208ba99fc8640621ea2ee85c8bc7db8f375e77f3c92c0a0a050f1ecb8ee9822109321486791c16fffe8a2f2f71063ac8d324512396679d67d298f8578a548f152022ef8a10aa0c0971082aaccbb2c7a216187d2ac280b9f02b715dcff6811fe8ce2966ceb3c203ece62b4bc22ca9dcc76e9346ed0b7db2b58706c652ed416f245af0bcd7b1ba2373a8583bbaea3cb69501d8076f3cc05e9f76c79beff5ea7fb80e6f7e46412627cdf41bc63367980b2530dad40fe553cfdb4d6515ac21988574ebdbaeaee4ef08abd2bf7ba4206c696626719563816420cd89786153091a72fec853577d62d220139262b8c0d07189745e2857d98cb5f41e9e3db4589862a9b45af5f9db125d0a5258d4a2ba177bafbf991202b48376e87b874c3989bef55c8d163343a969bc0216e9f6dddd347f22911aa6c69d30efc467d4b3c09dc7efeb480083df9102ce139190b086f4962b98ce886dc5cbdfbb120870888a2cbc075e4c070d1dd30b882f48fed8712ecbc2322a6d1f06a4057ea984eaa5410049f19cdcaf1726b787c404f97d3b5be1b3747ebc4f71c527365971802367c6b4abf514e538f341f2de1a0b9bd694b9910ebe4e8430ac00e653c9908e47473c8dbb9b40dbe501a660fcd42adaa7e5ae057cb2c1e4cb0385ce4a75bef2f188729c7dd1dfda68499a0ad60606ef9203e405ad7dfff2f1b285e6ff2158cc3e2d13d309f05d5c32faf6b9160b1d330571cfd0ad913b478b96e7c2f7e754986fc2a234f56ef2ea5684a98c2b2623d006ebe44511ddadfb256783e8bb72e4901e6202ef04353565f2c70bbc8dbd0ef9ce82f8259879ec63d155bf07a4794c13b45a1184c2560ddb919801ce89cd2ac8684fd323daea0b5c17c8219d1a1084a977d204cdb437ae85092ab4883e3a1039aa3e3c5fd23a480e257ca713f4fcfe653f61b831650abff63132cb8953a623e4290176176611a85e870da349725875429f87e3662a643fceb7ad38d4d35b2b10eebe46fa5edd61bee1bdb65721dbaffc8b79eff6023e999b304b268309442b45f6b447efbfc047f88e31fd425082f20e49f72401e85c6a751425a7b7782f2641975f1ca298925a38e479fb64976daf9c8d8b00b13deaac75dde1ae0c76e52b16243308be9032c867d2e8ae80cdfe53b0a61dccf4d2b5845893203ad9122f18890672afeaa0308ecf62d1c4024f4e75cacc305aa84aa00f319f376d189b4b0c1b4092b1c4cb3c45602612aea568ceda1308e83a88e8fe80997f67509cd40404295f5c4049b94a19d48f0cf69595291923bcedc1c4f9ab72b5d0240efe333b2c666c2c6f054780b8565cb796aa9d990bf509016ddff66a8d4a7c2734794d2e20847ea1967b5a8270ac2cd41f5735ebb2edd4a00a994213bed17f2478879d5d1828dbd41aa19dacc8da720fb53a6370ac57d35c2dde32e0b1c8b70df1bec16a178d177fbeafee8ec424f39be4aa12936c2c43a48085494de97bfab1d896f21526c22b5aecc571915e040c66c7b6ade7fb9ab64bd1bd08ae8af7efc6b842a8d61601f222a33822495d7599e92d99de51a89231831bb3788e82970a86a5f74764130eacd31620b14764db2c997c2fb4112f346473032d25b9bd1c0c9bd20f591d270d7656f395a2b1170879bd0d369d0989cb137567117e0bf992608eda9c923609b53e265f92c3213ff40b322a9c3aad4d9471a8bc83ba88c8a698e40e4e477b00db617498833c455b2476cd2e746ee1fe81f994a8470c1e0b5d57a04bbf556c22d9767f0be85d1d9adc04c24b4d099d3ac476a3faae48c8ba5183e7570a22b80cc4ceffa0b31dceb94d87c12dcf625dd643e1c7c565ee0ea0d7e8c107b3a7800f38e392a0af0a7611fa858b9b48f7c6c3053a70ca6d2ad176b8ee8cc2654359193b80483559c401b3753cd6f70535af84cbba71dfcbedb3e517278b781289bf2375b2e90cbaac720456e71562eb313252de60af2f645a61ea04a3e94d163cb00aa132a85d35fbe5595ae02bbddbe50c51744c40607e7d554d37e808f78e06b0226388419981cf669c9a43f3dae64e2f0531115b9028ae3a8b7fd8f8a59dc6bc14fb56195ef1217572d3b47a3ae9b32c1cfbb0c123826178d56f8abfe673941200ad3d4142d786d66e93e5bb8b6c9c00293304e4e12ab7bc0bd3378acb4e16c00e47561fef962fa8e518f98fb54c871b6a9b0eb7df17cf33374b863838d832fb05688da4eb3e9b897a4505cb15b007dd0547bd6ae334c2a78ccac748c8bd5f9f0818cd217f96809527072f3d2c241eef7bc1d6e3ba9caf55e362d988f0232d4e2d28da0aafba171e826e045bdedd7b40a2a4c40de185f625d9772970fbd121f308be46062e0e4822a7343af7efe500c02ee6db0abe31b7487e8988f2bf8689c60b0bcc2bc10f14292ab79193069b345490a032cb8dc9edce9974a5eec218ed281f25f89501fd3dde0f2640b620ab0e98958b5abeac175e712002d0a5b0f8f0f900035dc00c007ce172e2b2400426d0545968f18a657b687d2495cc0e6df742a7128ec49c7c7316da38fcbb03c70b1adb709f43a0220100a087a025c1dac23df3295dc4cef636221afa43b7558b8eeaab48c8df77d983da654b0b8cb9e4387802299e63a2524587359f6f251b1c0c9d9d909530d71315c54c836bf6459d8427961561f2dc1fb29ccbd03171d3e77a9e7be88573cb68be89a606c9364a6289be8e2a0d507b4399ed3a6e52dd2def2a2a370b247b27dff31a90c4f2d7ba75b95c1404150bcc43daa10dee7d5b126bb2cbb49cc849f88601eab8c70e94acdd8e118c2ef804608008fe18173850e6a014b052f0d294b9e55b8cf15a98aac260af355a0ae54eb5f85446bf3531043b553f86fc94756e82e4d323b33ed14a74b095f6881912102e3cad528016fe7971f4fc4bafe36e9a45e06cd2c9153e2459d028badd043f35f97762238ed1a53017ba188e8f2911ce6823bc4b005a0afd691ae2866724688f7040d03c331e40903034145cd9eac7115c4ae669509a0ebe45b8a156fd2c43e4540c6fc6d6395e206e073f5c112c247d98c7bed2e88b1eceb1b0a32bb90468c81e2254ad90619442981f4fe119372caceac340861ab3c422d8db863d1c18c118a77a3e50a8ecd4de87e4292decc92a138e62b6940d0185896123947994300009130005cf89c3f10fdb77d2d77c56ab353e962fbfac266d97c7377ce04d27f3bc5306c169967a385992970ec25770e20b02a24f4764a55d0df286d0c0471be008b18ea4ec1687226e63f3e36e8dbc2e67ee2668ec8e013afd9930a7c42e095c8044a262bc7ab9df877e2c06acbdcb2edea07cc85137b4035a8c9eb03f57261bf34da907142071d5bd89bf1d82b1a940d3a439e21789925d5e42748749ad0ad6f71de01d2da2038c827eeb43a0db8ffc7b10a9d07f9b9aee83793c84018d2f85940cd840b57a0a0b70b1ac2021377a8d2ee518fa68c47412fad7784a500e172b578b0f04ede547086741ea025f347a4b8a0077772bac59f9903675773cf0274b4cd65bb652fc10a24a646820e0c1408177dec93c2143dc42186830753675240cfbc8c49cacf2e283cc5878f9cc793e31b8d81869ad0fa022fe2da056a0b72fb7888206401eaa082f9d2614ee8f954eb42df400a4f5de1390697ec43ebf2d287220deed56d8c48a8ccf3a1c467d5d6058ca4999fdbe743f020e2197fb337c249b25b550ef621a3d019aeeed137bdae1d2fb3cd65827f19d101b5347a572f220d9611bde456ec38e8b8e666cec80522f4600f7fda0417682337e27bfd4b9be033efaa36d5ef4150082e6f1d0160bfd88a954a28d4fddbf77c0e94d6f9d6c8f2e3a83f659f4e9c0a46d198e9ba5394eac2743cdd720b1ece0d9abebb3d80d6b0b9ccc93d8bab26af330f7a7e485c40081f5f9e562b72f4f69103b204b93d9a9261b1f811ee90a9dd2265b00beed7acd5304d2c978eeadafdc8ecdb8ea2080592067d5a8c9edd7247bc3d9c70076cdbea4f04a91102e7d5d89c678f7463aef1f1b36498a303e8ad2584265749bf56f6d00ee575bad966a4f03ab0ae32269c8ef13fe4bfaf3562fc4051fc033c79a98cdc18396cb194193d509b0567216fb374220e93d38a5e620a7eda7a0e648961fbec4a314009117d8dc1a539104240169f3", - "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c18c40bec999a837ec1f06dcee05102acdef50c501e771cef739dfd0a4cac5e9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000e40957462902168235249211dd6ba6ee00000000000000000000000000000000f8defc3ae63e7ea8b9be28634acc72d911521e0a601b59f0fa9d199e9f861874db5d701a0c38f6d725d9fd8c1f0b53da069abb3ab89cdd02776ca33f3ca6e0d10986b7d8cb986d0f7483b530eb616eaf1ff0f767423d4fb0ac0d17aeed5ea083d9f2bcc677df5fffd068a9470bb0650a1c7f0980ca62990220ace07c9bcbc630aecb375879e49eff85b7b847f9809772003f9b2ed5d6ab5749b0ece4b4aa05407fa4ad7f08b38aed610a3106e903bd182a2e15e0c5d25a298fc02ff12751572e031a6b9c5317a80e14909d881df864a51d05b35693c3c8815a9bd00583fd0cf9aa65fb3fb6fd405d1fd6696f70a029c72b1bab7f7742fbf2e42166f7c9aefe3be4a66c6f0b54f222802f6d1cd2104f82" + "proof_hex": "0x000000000000000000000000000000000000000000000002b30b0403aeee459600000000000000000000000000000000000000000000000db8eebcfc671fdeea00000000000000000000000000000000000000000000000af8f618d2adad1ebe0000000000000000000000000000000000000000000000000002c5040e9c20dc00000000000000000000000000000000000000000000000a2de63e4ca6eb0e210000000000000000000000000000000000000000000000021ae69d4a65c7a8e900000000000000000000000000000000000000000000000a005bc241e7d733b30000000000000000000000000000000000000000000000000002aa8c45dd85d4000000000000000000000000000000000000000000000008ff1dbd0f4b9b41c900000000000000000000000000000000000000000000000a46e1fc8b19f097560000000000000000000000000000000000000000000000075006eb06b9214632000000000000000000000000000000000000000000000000000093433350ca3600000000000000000000000000000000000000000000000c8e6e0c4281f705a50000000000000000000000000000000000000000000000043c924cc53c6b0e6b000000000000000000000000000000000000000000000009a11c5c7358610a3300000000000000000000000000000000000000000000000000001c36379475c900f5f028b1ca9b05f4fd64f348afe95c048291c2608303e91a5c888446ce265e275b7caf50609f9aed4ce3fdda4988dff1b9cfa755d22a06ac9bd70fe011311d3021f96a67be4ae494bd7f310637090a697383e48e1cfe83d199fea464581f5b26f5d27b00505d5850189be9bf43d8b14ee58b28a16c87b3bf9d5bd3dae341de20a977a63187eb07fb817d6f32bd7e353ea5f2e4563e05af30e41de8d76caf2a05fb7411e0736765ea872b88f93642e1a0a122c2beda192429812e49797d0d2b0b2d57c7f9a3d89feb36de6e8925068d4cf01c5631bba6ad66d9dfea89eef42507b82d0cdde68023f51cd886423293963e553af03761d6bdd7e82c5f3f70581726faac8f74102eb6129ddf5940c15a1f473a25099ce9aed7c8502f304535e73f02c89d7edbdc59430a931b60bf65dba6ff6c4568fcf3361abd1e9097cc3446930d256472545bebbdf0cd8202744ebee6b32aded93bbab7c8b07493ffe15d753c2e767d9b71f63f422e0d8397639315c74a3f459246674a141133d6068e91cd04236270fbc4be415c1b3fb0340ab79c0aebf168d36a35a77d26f5507817a381e82997d3d4e6bf47cf282de75db04e846b05a1171e1b27b3378df7f46c8a0d5f452ef0980db9a94784dc6878f8768320b7a5f1327d5a0398b9090cde4a0301320d2c66315a1209ffec932d6318d851ff138899b2fc2f20210b9ba6487e63272dc9292a55757e58a10eaf53bb32db4bd956cb6ea7db2f7d3d376ffde54a0c481c8e2b91e9e7d106a81edff57b8a49e69ba1cbce8d8ad7a768f51040cd0760adda1f1b057d97309b56258908e7d0b72e311d563d0f33a89b025e9b29dbae8430af6c1d5dc9be373e533150316757b50b3d7f39cb3fb983eec9d368f3997f12567c3516a615929225c27f1166fe0b8a910ade60dec004d39048fbb5e348d1d7b8f14e167cf8625eabdff7f7262e220e7cad7c3169f11ed47467b15790ad046ae3ea490d5aef76bcb164504f3b903fdde68d660bf30a4a023c0639d3b0df5986a898291ca9055a779c358e4526204e571e880645cb2634ddb604f97aab892dd8b2c61206a8ad742b79fdd1cf600bb1b100e239a25d525acfe6fb97a5c0a13dccd21c3e1be9395c5f4a8ce11c7c9adf0450a3dc13c753c8b0d37894fdb03746c10c45e92b11c11485fd5cca6eef8495c4fe4a8e89af5ce2321dacfb3de59466cdec2510156f196cfcdf53325e7070710c87d4451690eff29e4d32a8e34518657116d7ac1562534b4b1a9267d5813b831d136f643f2511cdf969567bb8528b3b509274e022485fa86f947eb6caddb7d6aa964114d188ffd141b79fde4bec77997e8de82e1a22e50c704d7e5fa81ad6470e6e610932071304bd9547f446e615d36f44ab582ea1f51c35052fc32ef3fd4004dc82b1bf9a39b670cbd3064b42858de6d62c0e202f260e6c01dc5da58f0f5ca5aadf80c9a9810e700b5342d312a883895208ed277759e4d19186345a6d59fff1e1b2df1b9ed20c49e08dba5d430c5c889d232c0dbec12765d1f90c1243fec4c3d67e1d37e80f7783ef9a1828baaceb6f59bad6211ce93b78dc48f06df07f27a2150b2b744cf2a5a06a0df954aaeec4161a424e2eb3a35a6b9f4e985ee41ca8ff60b38bfce07c646fcb9cd13c438c57afced26e28939f478f3c70bb968c92a82a5be84d9453b1121028cc087d1d072e7e6c7c0205fe83b1a01ceba1e0e90bcce21bc3dd3be897d503f20f49b07d818120c8883900aac83216fa6bf485c26476d03e6cad5643b6c5cf217f866e372b1ad25028ac239027d059b6b09551123523e2a5f42d33c4b330bf9b77e09785e6e48fa657361dc82de67104ad93500c9741939e8ba447f6dc1fc4191770b950772b9f1c82772b810282ebc72e0e003f822c1a213b3ce88f42565589de9c8a22cd5e3fbeda3c210803bc3b27db480707058c2df76c782e29c4ec3ed4258affc6790d85b95c0102af61b154df03951ef522e8c2e2d7c0e65d89bbbac33c8119dd9bf436def3cc2b3767985ed2bbab59f583232694b3a362918f9f72a99af2f0f5af2efcc7697c2d2fcaf7859f1a53585929269d86b3c8973039adcfbbc308fee491644c480a7c27dfa9fa5ef3bd09bffee4577c9bbc3690003ea1e3aad93e92fb3d3049d533190deaec9660872e7fcabfcddd95bc009081df6bff849124e9e9df109416fc751b0d2a98b66c8baa3500d82452f976c890f25ae6399064f155ecdb915f4d008b7a002f0bba3996e8fb749742a4b69120e029e62ad24d4f998cb586da076c916cb321f43d09bf02b75a56631867d73c77cdf9da4de3492a4b650d78b39b367dfa510fe1e9c7137dda94b17f265f6938deee104649c6163212b3f9be3a66234369c113a8e3a54aa3505c8bd668b9bb77dd5eec4e994e0024de8f3203d92f6f301e1223b57e5d4a69b68f6d9c57d3fd2dba27ca0bfb145ab0c29922b9a2b129f0e3f71fc82a5b8a4a881948245a642bc118320f72a2723d6a7a9ebb1bfe185529acce021f5fa2445c3744c8ac48355df7a41a8520486e45fce375c3c6fbd3b33bac3c2e5e29bc45ca7fd84f34893fc756f888c9db6e8d1b33d731af38f622c11c5e180602f9f504ce236d5dd7c1068c9f472f64905dde988f8e4ae7f199def886026f21843689e58f2f3dfe48f8f5a637b4a71284fc5706e649278419b7c8e538926409cfcd311cf3c556de1e275dbabc9e67658d311ba3cb5c17542c186a7da2ffd129684a2f9bdc4f2ebead34aa0cc6f94cbab3aabd66c8fd8bf65350be1ef08abc28a673680e3f8c7e41567d1ded0d1c173be5f3f0c7e080b2f1545630ef9644321ed718522ab1f7584f6a4ad83079823dbe5c55b19701cdf61208e783f6516be01412b7a8746a2c86c2cacdb91aa020b8d1db64dbc78c4889165e11b4aaf173202bc28bc70e00bc24bb8cbe3839f97793b66099528624e7d1c7fca00b911b707b19bc81d8401fa754ba2a05bc955437d15102dd440d1969450617d725d1c0baf51b3d8ba9c5943f28453d574f21fc2f9a08f9752aa61f9188a9cd8574593a82ac1794392b927399e2682bd77ca428316d1a2a77fd52d12be4d3f7fd57b9b33a68065b4112e464cbb09ae6d219c9acc029a295cd67fe60517b04596e84de3cbd1602082cab3609a950d6731f100664632ada637279bec7ead801d854a2b92a322b0f0f6f0554856fed457ba9fc25583f8f1ff8d03d55205a052c67db1a02282cc80559294b448cbc117fe65a32ae25d61584c1993159d4c2329f89293162f89ad02d91f842933045e44515de74b0f6da4625efb734d885a51865862a4c2d8104682c6d83cb7999c411b070ea3a5bc939cd54c26d43025654b66025c683116bdf06294b2eb9482a612e3d67a632d391609406b60edbc28b9c01c5d5b43796bcc3d007d36fb02537fcd6d3baa7bc9c3859846fce0d7b30d65860a5d8d27f6d2718ed15850e16cdfce67e1dc1d63b0fc45f1a17a0d9c7db71f689e3766e12786b8c2a2d60dc28eeadd07ade9fbf8447fdddc4f91d05b10d136058ad481f9b4a7e024a17029bad7d3a24366de116e8077da5b101c14da26867735d2524bcb9864778e02dd5dc33744ca7e50e87806b9d773d44dd60cac69135a5d29dcb4b93fe58551024ca2bae5c4c5f6682cb487546e40abe929304b714f50ea72c453cd38dd5e8692c2ae119ad1d9ef3b605e627d35720d26cde98e914c811cc457ae3cd5252b79e25893e23371561e89a731da3343d9ceeafb13593ccbf355ea34eba49f2e63c47289f58bd3e1fdacbb319183e85582f405592fe19656cd0a23a1cfe79d906f80d1efe414b0a22a209c332036c65cc9d46751c183d8618b839afa04eb21499cde40a98b16c72709e82be7312a6085b0563e940fc80e1cc10ad90615a040be766a204743f7867ec779c8a1d0d5fe3de9462f19bfdb3da14dad8768e04f13148df882eec36da9754f273e15636d808ea116523f0d567b5316bd1ebcf6c4de936d03908774b8f1d132154642b87c20ac9cd2de8088f1d0a55de6db45a75543d1adb2806b5975df484e4db990be106045d32642afbd7a492681e8681c84ec41bcadc6e1527f9ce6a7a670a20f260ae45d21aaaa489855dd747e5340f01a956152504a31ebc51009014c5b28e9e845c3a143d89302c81d5f94bd499250d48ddaf47fcec0afa7a61480263c5273b8acc667e784402be671586cfdb7b4db3f8a0f5e1fecd2aefbcf44e5f04a3617598869909c0aceaf0951635951ed4180a0a9b50cca1bb2b5b775f56a5b46a804330edd52fcbdf477485ad2f90e1aa0e3c6febb4f769eb0a4e62b793e1ac9676a35745509b1b9137cadb86a1fe28b0a00b1ede0523ef0229d5521820274634a635d398df9b85908f4ffd8533407e995995fb6a6f4f8bcf15aeacc9028dd5d83f265d2fe95d44afb3baa593f702b1937c3a27a40b6f02d7058a16f98da0cb85c8c6cc07d602960eb28aed5a6b5a8005363783c4c6ddb2010fd07d26daf701ec005d7765e9701cf8617925e4dc28744355b61d1fc2dffd272b74a06f3520227be984cbf13925584c110fd52798ddecd811ade62f107d939c1379e3ebcb8d71f09dfa48f3f2be201f2cefcc38345ae3ced3286cd06dec45e9115b345f3e78a3ee45db7f9170064864d5c09cb0fc3e8f5b7a763846c9b6a87d11798becb574a394dc37646282a7f7c7db3824cda09f9f8a5741937915979ac90b4f7b5de4d1eb4a3b7d6181a242ce8efe9e99513e83b53a66d91e85ac3412a40f2ed30dc99975a2a8aef2f11dcbb15473e41cbbc7bbb7adcfb89f3a9aa708170af029423d5e3e33e5bcd5bbc040ef8843868fc9243d05539ef570dbe1e10aef20fd382c33955f4bb9721ff64b91dc1ccb64d1c39f422bad3029fd747441ead524ce950b1a581caa7944b372cb78c0f9933a173143d3c99f2ef5203bbd71341e1b580d87c30fdf3e28038e52e088ee5d1729da0d34f16b6485002e49708b2af80a0440fec31f89fc13213af1d473835795cc991ac856248d6947c032bea69585093d3901d6393263c2e887bb58cf5f9e8dc4da7a20bca245b87607de9ea0620f096ccbcd54b6f3921fca613160e8e406d7331807a16d33650e418f91a75a1b49155d96ae9ab1de581aebaebcb2cbda8934bab4be6568e7c921e6ee1e9ce8ac5b280bf12e8b4976159f25c05e85fd4f48faa1e5042455da26784f0378846b00b1019ece0c5443a7276dce936601960c620c0f48dfdce3d7158691ade1f4c89e8e16e33e4c84777d3b261850f67e4cafd59caabfaea137826ce0b785a1efe2a1e308bf9cb95df87413d1bed427d514f8d2f1ed5215cc8cc01b960d2fe3466acb780ec253f4c5b3916132fce27c7d53c269e49e0d5f5cfc302c1815c83a39a842a4196b71ab05ffccdc4453eb6121cad50beebc9506f5e6e7a86325c089c1505a5d1565e4e200c0f947704862ea0bbb30d55ef3b0138243795391ea5660caa47102215bb22876b29def7ec75957f10b4173c7f04355665519b869e5d51c8f7e156b05af22ec3dffc3be18ba8bef4b6112006d557dbc866a984f59a6b4850790c67d09dbdd531ecb6baede53b5f37ec5bf114667e1d26ff037959d61dda4206f612f01df169922373268b1e7e6fad457ceafaf8c9c25e62c37454897d9732b6135712bee728c4eebc919211002d9c6f8735af7b8c0a544391a46f28c3a889d0f2e7a104554aca10104e8de7cff3649c20d0b1f54088096a7532b5019e476647fa05e2080741d0dbe713cbcaaa2afa2303255df9d8e0d391a6532b96bc449d704437222d0edd1fc730ed424ad8d4b5a39369538c7d586ba3e59184dd5f2902ed1146e1a01c7b01250ab358965ae82a0f4528fa5ada19814df0d73e4256eb85c0782e527f1f4d0221ad31b598531d3812ddcc1499f25e1501865e17df02e1b8cbc9909234c2802c41b8f3e59bdcaca7bca354ac8a06eaceca80e576276e089cb5f29e61b0d1e6d85b56c11df39d49c0836c0a3db33fd564f9b81b2dab176127f2020091e84be762866d62955eb34adfb2c036dfdc79652bd5168e1eccebbc106c6e0842a6ad0ccc37f360dc838e8fd4104c57d8d74b677ebea4c070a1d5e7a7fc219ad1169ddb19c6d2d9d077f9a31b480259d7c4dfe8e603ec1953634fc56a67550bf2f4c127151b656bd10e7db9c9b4e19e526d93e483d2beb6d6f95d5dd7dc4ef270edb572eadb4f1e9abd0639860e241ae44ee499db89e92a7971670be22941efb0fffb8af56e85dcb0a48c09ae10131bf2a64b0ecbebfc772259c580506db69ac2156fe9c7240f7f66d4714e18bb20c07f44dc0cd84f0d9b1aa509b66ea5e255e0648ed0a2962ede02665952151a54e8cea60f1ce897f00a3abdcd6fae82e74b6212fb5e7fae591d70df03de924b74918ada838ac568031697a44c273484fdd102cdfa567eeb6b89ef0d9fa657e8943950256b1e7053b0824e4bef3666272ac0314f463083e7bb01bf0a666e722b81cb549429070f0cb17f6cdbd435b5a9beb8a2e4d8baeeb9fc7abcf248cd7dc6a01075c51db5fa579aeeab43a582b47399ced1f5044f92a873e6418ba412cfa0a8a2db3314585b220ed790f35ea358f93a7630fa379be8dfc67d5eb98c3aab5c2f533d6750b19ea957acc6bf9e2796920007f14afd5d25310bf4bd31ffb348452c2d021e55f237a212ae30aee3f52d514f4520a9af18e31091497248753927f99eee28859559b268451ff74aba9de02ee6b4322d7570ff915b07f2cb9c60a3b39cd3b93d33e3e05f149db531e7da3325e7bbc1384880b0aae07a0d00123c89a8dee209bd57008f71bd1a6a5778e3dc257782b1f4eb8695e6c34c7d409d48f8dd796db2afc7267b6c14299be5aa8fc680abb4d14671db1342e867fff13ead25dfd0ec4deae57cb9c3dd91d62eb03725d734f841d9749ba103d39a29deea0e29fae6d44193eb8daeb4b06ceb487292619b51b15221ac506544d7bbbb235a8b1e025e6f95a55d4a9da6e2269cd0fb34f9990ebdb13cb608f09dfd348a611efeb3a20ba7d4911414ef238b948281e71b4aee677131b1bd90b800ded9e14d190e183fc69564ae4f8e218035a2ae98adc09e2dd8911283d549ab1fd630223ac9b69f13ebd7edc52739fa0d86ae7bc7edfa3923fb49f01b6905210ff375d48be2446ec8ad19fd201030784ae30ecda07d819104ee91e060649b3d0c9e9e2b984ff05a62d46c6e9bbdff96c343c82a6512f86de130e771a1a878e6a41a6ac0fc0788e6b068a9acdf76a9e550c760f1a7d2054f006a5c71ac65c82a640ecfdbd3853275aa0ad3206b715cc3ba60919d7a2d307a318d84729e3e17fc47339081b3733d471aa0c4a156a7b54b1b9285d483229a32bc6340e223d87b4069b04d0fbbc22d2a54ac37af4289e9138d2648099c28729bf009c2d28faf64c8a8042fffd8bb899554655cd463010c1dabe9bd2d7310594d704f1e10ece778e01fa0d14bc72a9095f381f288abef48d73e1c8f70323019bd3790cf90609f16cf84220713056f45f8783d40e5baad22fef47a7e37b46ff86b16a7050120dd8a0182fefb468bc5f043da2c84b3c3c1233743dc0d3f812011ae063c60c20907d674016e758411738d08cb7b38ec5cf2675460d6e4979a92f1a6b69a6d80f09ae9f8647a6057406542e4b5cad4d751ce4851fe2043791ccf35977c9694b27ad7a862ed2c107a8ccf062000dc496547dc6b4d6f3b0ed91a689dad868bb70009cf51ecd1b12f4f015bf9b58c6a3473e82736e632d1274d46e8fb9aadc92ab01e0c7c4c9d3ffe24d2a3ea98e88d74bca4b8fc56d7d94fab738c555f2cb4ab724de351101cb87df73b68d48f182811d7bd724087eb2d3bf773a92ae7fb3023d05f7d92e780bc1b98e1bcfb57bbed3078e50104684c04a0297a1f054585de92e285d2c460dd30427ebc79eb4e26c0479727e1884df19ef51e10dc42db993de31190d3d6c988c1c3dfa4e0105fddb7ac35563ecc72a4a196e035f91148ec48a4b119a13ca4a98e0f7a165f6547ee9750bef23cebfb0e79c139d02c6565e92464618471f2483429f5093edaca8b65ec2d966a66317e4fdfa8efb3df367f2511fd514737a1a937a481e7d8cc9a03c3a60841a4bf7f9754bf2fbbe5cb6164ab948c00b2e306a251f23b43d7d25e038afd9bb94e90efa334bea6c0c33b9e527b154f0271d9823269f3414e5b7274cc402fcf960867cc389df326a885484a21a1730d30e0d40999fc462c52b6a00344eb43a6e589008ec5dabc7d3b7eadc04cef996180edb9f5dd33c7c3ec5e30aa494b252658237e1f49445b383aa5221481bf279c824a408a0cba2b07ae7260ac3a8b16e1d481d8bf795611beb58cf4994317e4a8a1f43bc309f0b9a6938bec0f485cd9f959630aa5929c9a94e40f0fa18f3ef52d225323ffaecbab738f0a11859f83e7823fab2368e875eb74f7efdccd9d03906d70bc078f6741689487c98d8823c61dfe17c86e49cff25367cce5f92cbc602a350181120e505a5904a146484af259d3cc7407766ef0f9d0ed2e8c8eefb1455df430b15d7a20b2b8ce47711235c340fbc0f7cd120ab132ce965274b695e8384b0af2f74f1a79885f6871a7fa28a0bc7c26363be0e27b50ec7c8fb14c67e80e2b47118205e0e8472e0dbd61b66277d3439fac17951af5ca2d6b5af0d524ccba21b6b2a6051dd94a1b8d7e7704680ddef09a1b1c4419f2a9ec9820da589e9b20a70ab24d89962987ddadaf53f6605cd3710fa08fd60f3daab58c4ebee1ff8f0c7b88616302942ef09f82722e003f1421f3722d0df287cad3f920171162ba91a98d6032d74219ce8c4fe3c7e33b0ffa14c83434d58870d116e6c36559a0c5276bd3ede2f133f4b9b09ef25e76cc4ec02d3a62df652cd9034f372c30b66891e912e1bef1e17181b25d1ba2eacf2827897b7b2d0c5995ca072785082d059c8644febf9771752e9b58e4c98cfc60c257c9111c8b2bf6734c7ac89d2bf8276d6eb14a2be82084e6fdf8ee543c1a3922df2d173bb2d7a7124e55a2d39ea70742821f08726c31b6c80e32786142546b5c35a2a9a2441745ce704f0e55134024b64036bd28f8d2b8119e8dd0e5cb9245d8c9058a66731ae81cdac1c54debe97ba687b26ff00671d6b69bb7f1dd005c20971d3f86f8949ae90483fc8972830abc6481f17f90547263749df4c5def86d1923157f67fa1738bef66ca4fa004e6fc5b4af721837799245cf824a7f53cd08d243f550311fd4a3d959daac4cfc85f32604bb35af71ca004ed6051ffde6dc1b4fe2eba6d82661319e9cd3b855ae418d73cbb37864fe3c10c42f2fc3567df4971fe7a1774e33b8a72c9ff4a7c9c666df10ab3fc86bdd66d2337af6fe5478206b02c5f1709d87b9df0b8fae401bfcd98b33c30e01e93675f1f355765c7361b84de13e9aef9e4fe7aad73a2117c05f9714a0d361e693470c80506dc72a579afc28392a084af4bb406e51d53782e6ca9ced5f2733ad62194041187cf6819f495e3f36941d42ff64e68b479b4c98047b50d14e9d906b8970b9a1a85df2ebcd9f91bd9f9e23d6a103a4398801cf9fd35ffe19b1d22551a09c77e050ac86b551234e153b9b1f200e5e4d128ada63380f68aaa80df274d0bd10de6129ec811001ddfa3cda1b108abf1a10645def9a8f6089074ff283c624782d8e507e2120f8237004abe881653eb8da68e34bd3aeb10e4a19a8fe7684dcd8fb2160e98913519c62998314bbad28005c4a83290a62f9334bd58031fd66bb0db262019b4cd0c0423184d42dd0524ddca8871512144dcffcd066ff6ebf9298ea84e812ae69c3c6009d6328449e1f9eeea8fecb6b9e5c96a196891777b729dbcd009b11dd6c86933cad747984c5842202dacdb14319ca09f16950b4ffd8e8380fcfa9223d511d82ba615fd3506e86326f1cda9dcc1cfd9e6d5f1ecefe79eec809734300c4b915bd8b2f50d9ed1228836ea07a31376c0e1f4bfd053de47bc04acebc1fe0930c6a3476edb691858be52b6de2b01d9e1e4abfbf3d3c5d2684b4bfb7c657919035941a2aae3ad42a9e63465e499c16d2dfab35f248622e7e58e84bb84b71d01e032044b717cab2d569ebdfe7d5097752cba06245ffc7cad1b18d79eb62ba3118c1524d59d9cefeeac71e447831f83c6f290ca10481e6916ae350b88d60f3e2f0058bdb51489572e95e31fd84ca7260abc39f36938b4f87e6d4835f1ad5fb221a652e8262f018bfa3856a8cbd34144e6976d45a453f3ec06fd6e376dc4cee60c6500c3280e6ab4927048494018c8afd9bc6fbdcecff3018ccf7d03b51a6eae14b7f8520c39787f70776235f01793d7e7f745848413062433200ef7f8cefee602784ed98b2c9d273cdecb02ddfa18a8a661d741f3bf87647e3b8d25eba2360217f3b3108225afa2797868ceaaa866d663881a0008e416c5f2f80af7149f93050203465a7c2fbfb1ae9517fbaca023c9862be40b2c84f30b81d237bd7c3798c30891c8ac7a91dc7b0b69ed9e0982acb177108b867adebdf348d894dd510a0b7e1cd010d9147a550c8f12f60691d111628d9182c83ce9536af7ead503f2d86b1b07c40fabfd7c29d47e4f8720b7806cee007281fecdf927c5a8090fe200db873a0142f5b631901ff537e84ad96b1796bb469b1b11cbe42c2610dd49aa74187a4129a1854db8be04d0079d6f88c31d140eed4d5a1139b6cf42a9b792ae577cfc7a2879bf32ae3070900028e0e30c27d2c5d1e44d731bd3d2e6d6135dfcfb18572d107551dbd61f608dff663d88629c18be0406c8ba65f8ce3422d544efd0e1e9000dcdc683eb0eece6d0ba89d46022e636bc1ca13b2918d04900ec63e29f630e7b09d795652cbd6a6479cde8cdeedcac973f5d240002f8f6df8a75ecb9e1c6d2b609938753bf2c7da02deb6f5f3bccce5e9ca2afebf91feaf1265aabdbe2ad99212954377b6f55c3faa8c03088b93f943eb08faff93d5c958f119889f11fd09ff304933fda02023e503321f3311c3248fc12cfcd7e748e21b1002a39c02c01d09209a0ce90d8026f074000e455d119e776d349ceb3da94cf523de5dbcff0f48b0c1a8048441e7c85fcfbc208ede9faedcd17cc3feecdee4c70aa042c6e441b059a14cf0077b5c8565e3c03b57181839f9f7ec98fb603c272189a6ff3a1d3a4ab621ee17eecc490d3d3fe3b69830871d0f9879d3049ba97804e641d42ee31522ecf1859fd0e2feeff6c4515dec6601e327c6ffebc6958cdf901c0ca743f82214679087d9626e792e9ee6cd489e79bb805d2da252d55a99150fe19bfb245a1fe42170dbbb9e93f584301289b415aa57fa49a31d5a2373d55a75df54190b1e216324d0c11ef5e89f61d21a42d90763ef5a89092a10e1acfb7e523d7f3fe9f8efbeee02c371b165637262eed95245171b22f53990e63e5d7277c2d73a4c391af7f2e4e1c18101c45f8cae2939e17c2a4999e38be4e8683139cdd427060e6f476809b670f35b2c56491d7f825eba12d6626da300fd3c80da328f0ea49b767a41a88ab8321c15d42c0af8fadf6438d42bf60c3a2b3adc5871de99e21556ddba86feeeb79168a27ba4c7f46b716afd29c915ece35e192e47cdff65cea5c35dd963a7b9ba5232bfcd3e8240bf1278b36d6aa6dcf916c22e853761e388d3f1a40a433aa34b506bff60e623daf6dee09ccd69d2781bc4d993565b6370e7684d7aaeeb2b1fca601357cfc35f67331a5714552e9eb3dd6fbf2119bbbca64ea76c836020d19ec9a1f48e6e38d20777c1c4cb8531ba222faec847efc90b465e9c34a7001ae1aae1a1b5e613f47a7d2a35f2fcaeb1539fa3306807755ed92968758d518fb4475f80a0a3ca9d6a6143384cc6083082d311bc387ed1b500266acb41b8126dfd9c93e1b1fbc2e1f174be1d7761604d9964bf196b9ee0a8706f863e1822950769d6662051d0bfd2686444c417b5ceea329e2584442c5bb2f75f8e7c180fedc7a3a6dc21014bdfe5ba28573f6415f77fa9325bba1727fa321aef3c424a762f29b3bf455f51babf7c357a4e5ed8cff49b4eafd2746f73fa06b1d5ed4bd15999b64d6f3a7121d69a9229292044de7007ec9f717fdf09dbf3b367ef4a7a1baa46b10b4e16ea903e4edaa9011039efcee40df9f889a16f1d4f4a9a29abd4c5d587197dc1da1d8130ef6d7f55840a3d60e13734ce26b321d00aaab7088ae1779d826e99d92f54b0b80606aa57ab42c90e751dc5b716baff6a81bf135f4250f00aaa0df2c833761247a34288a2ea47bb0bc836a6fef419c37495bec860fb6529465724011b4e58017dd1415346d52b19101d5eab12b19a0e441575d47307cd7dece7b39f36fa26a012112fcc778b55fbd14f16cdbd3acf9a9169d544f15345cb6173467874f657f11a12cb8d9cd9c39420f84a35791fac6511b78d593d42fc2b8f5d872900954a406331151227f3b4ab61468947e78586055c0c09c0b98362664e5e1c8975eeaf02cbe3fa2404ac419609ca705c7393296997f81a9c8423adfd4de864c57a97922159f4fd88748365f7c0d07563e4d453417656c8833e29d6390b86fe4b794250a151fa247a5e1b9c58149170293ce42a65b9cdd8b38d9ce0ca032a2513a10b9aa13bd582849621d7b26e71960d6ab34544826f40f4772edbf65887cf9c90675a02a24cd768068be5feb2b8020cab1f939df742e78b85890a0ed8735180b8dac5a2cb32325a25e5a55c3dccdd36db1e560cf06d8edaff94c8f8793df454115ab6907e1433a2fb8a9710e410cb4ae29b749c5a56812e5d7f5071e3f281e22439f66040be430da6e3d00d79a926e6d938d9e102c8de762f46132cb877c9cd0719ef3124a78afd322abf925c981c80564d0990d6a8ce664e2e17c87b4a0f4d853162214634684c7c01135a65a96a2dd46dce177320faba9c4559efd833f3dd5086ba8112b30bbf4ac3c13f437f5c875730809567be74cf8a9c816f4fd6716a9b82a1c0305ed2a51e4878eac2537a240468d0bca71bb3db3dc44587370c898536d53d80368b46b639edce91842b66e208b95b7592b85d9d72657432787fe6037eb85211c1c8adab3ff2f751ea8fbe3a00085f306569ec17b74af594427ec4560689fb70b4e6ed89f6eb53a9e4512918249b9551e0ae6c9f36ea559e9e512e2689d090a17415450b584e5c7cca50b33fac3b1787034d0208acdad636fc3d616d98f1dda10eb9991d957f037ea435e3319c366ef658c47fba775755c948610e7a384fdb026d34b79b7c65045b283b693ec39a3a461580d5e888e2bac27ea2c619e06650b23ef7817b83eebcdf4c1046def9755d8aae19209f9ab2556c7c1836228399dbd063a417e004937c4e8f1238d59628623dfef098ace7e0e94d254f75cf60370991373e1aaacd754e553d42045943f1356cd63e1f3db516c660ea72208a93925c913478d2a2d2d9e8f1fadaae136ac42187cbeaf41a1fecca67b1d9f8e45b8a21509b73b3cda6895c83a1346812b5ffe2c1a73db6cbccc95409b09f062caa7414f2a7b9182faddea8857099a424d6bff704d75eeb4793e53fd6c6b392efd365d02217bb0a66eb65c8ea113ee6800c30e14140d1db32a753de462a44320d257b6c021f6b2dd25f7507d6320a391d0b0de1d495960f32f421d11d91c7ea58f1838f225e75d81035ec78aae5007a650551a2130d8380a88a42cc5f00243adb65118f62bae9123494c72cc94cd04a9a48393508a4042ff5d4bc0de75cf5827c32aece92426e6a4df489f63ffd696a5e09c4569596d59d11853d305eb433044f7c58f0208708aff7ec7dfba646904fab95e07c593ad93e76728526f8818fd7aa58402a80feee7eb15a2f0b3c4cdb007544c53de0e1f82c11bd9d37ec0647180f05a657c2933fc48c254c582ab7479a9c83bd7c4f8eb0e29efa3ed8c4095fe08a1b447db0a0b2c4929f11cc7d7829662940098723811cecc8af257c9ca591eff5e55f15c1ea2954f52b5693fe6643d48e9f2f23f2143777b0cd7c33e824b1df67327692829046a46469f24a35f7a4a268c8071d3b08d4de93efe35e71b2ae80a054ae6cd0345a6af2d87b666f3f5babd0cd2158f9f1d0454c258d701393f9c43f96de381042f38b49a73be70882662b7070df771bc438452d607e3b501f82092cacd524912d368f485ca72dcc9607f4b1db83a3bd6b6e02d235791457fb5526e68a78d321645077c96714c920ce0f10bfbe8a0fd9da0b9366ee0db2b686811ff10cb381d13e0536e73e9b49c2341023a2ceb8651403207aba6394c9fe86be75b1f3c92342221fafa5b188657ec49279ea611926609c5a0a8b137d58691968eec74595db30a5eab47f49335e4f7b0e1c82e6cff21317280e2cdc9c35446b64b3c2295b7ec14cda916f25e16626a75feeb120e95eebd9754a395186204d03d9241a401df77194d9938769c163b7eda901948cbeecdf3e841abf27b3b8c92903ee36dcc5d060953aff4f2442313a8191adb4c9b374dd9cc5f08e8ba058b5c03dd111f489e461c1723525286162fc0c4846dbc36eac9cb8d32c1a121ebf3d81ccb322277755a2e55761686f53936d703806a52065735bcf568ac9d9adf605be45a56b980d198260445b04842fa9fd7ff6d367f50ed8553de4d1743d992577279a8d76586f479110cf8e5875e0c4682626f6a1e55d9116fcc4bebe725bf5ff24704153d8d7b94", + "public_inputs_hex": "0x2138b0022ab9af3c96b082bbf307bc9e79f8f1401462e3ac1cb3a9913a5b856c03d33039d45432d4999b9964018d8d6552808849c83503f50a70d594c01b441900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b202c2cf87674c88d6103108c767ab2ac48e35bee25bf5e92c02761b47af09b71a2c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851aefe0609e27b1f5e4ba46cd640bb7bbcf5d11024b93b2b80830d9827e59538f1b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4135a40b6144087f868125f387412d514b5747bfc6d2fe2bf59b31a94cba4d7b722725d5e5594a87fd3ee32e41daddbf89c75efb45a474670af2162a31702ad09" }, "decryption_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000761345ca7c2d53dbb000000000000000000000000000000000000000000000008b870c2106c8c9f12000000000000000000000000000000000000000000000006cd123d6787d5f61e0000000000000000000000000000000000000000000000000001002d68347fe80000000000000000000000000000000000000000000000039762a361bdc001470000000000000000000000000000000000000000000000026a5dad53a4a4b285000000000000000000000000000000000000000000000003e80e5f348c1f0ed00000000000000000000000000000000000000000000000000002827ef770aa5a0000000000000000000000000000000000000000000000066449283a973d6b2b00000000000000000000000000000000000000000000000d1cb05c943f85c5bb000000000000000000000000000000000000000000000000816537512683c953000000000000000000000000000000000000000000000000000080fd212dc224000000000000000000000000000000000000000000000009dc3fa8cb33581c00000000000000000000000000000000000000000000000004e265d65414a2a8d400000000000000000000000000000000000000000000000276e6a83335c8384d00000000000000000000000000000000000000000000000000024b509b4c9db80e6e27c837b9a1a1923e8c9013d3cac09f0b4817c01ef05bb32ea7a2d5643f702f3951aabb1dcec7be05b260c1cc9fe78192c4e7b28117c43405ec7760fe96771bc4c52230e3f98f1875f605599256fbf0270ddcc968a64a7014c30d7398512815fe25a1f3abe0923a1bcf8c2c5ae29fc488e5e1e3e4e7a819a5c6df4e778d341ec33e7903313269d4e06e2fa197669578c4b5e7af34d3f55a925092e9f03eff0d1a07cb6fa3bd31057dc9a4612869a43111515711ab3add668ea7f12502ad29268b03d5943e65661ea0785d1e229c42caae4886f2cfd9ad84cacd2b61038718174bdf2a71139c9f14a8a8b51ce845ef31202a179d34a6e4d4c17872da4e0b410a38c90ca076a93628f45fa382487869ad8edc226607ee1252cc23c673b650432d7c4d654077b1381ab648de7304fb15bde53fcbef07e16ff7838bd271f3752703ea62c5cf9e28fb278a5b43dbdecad28747c605880e0c85812fbaecddd1e97614fbc8992745d18c6548d66a24fc658a904f1e8e19cb92f3850d3db1c5bfd16b17ec5c20ccd08e444aa78a73e645d1d770ad63034aecbbb0c71e97be1505cec7103620f83fd4a8a6134cb599cf29f3a617cee54c97baa7827a476c0b657bfc9227d6461dab75c7b60827ff18e4880878ffd9953f46133793a1ddb4df697d8a84235f36effdc76e0cb2d11910c0316528b8fa384b3685cd6f77325dfb76db2fb10a50ecddb918375b561f762788e80bec98394f93e8d584597f9e47f81b05ca4a06c3303731dad5cef199663a1da0000ffdb969f87f52c7d25b103bdef03f95c41f9d847132ff0dad90ab0ce6e4b4f9e7948ff55a50289590c99ee16fe984dd9206549ec931dfe0a74c9a19b35e8b4ae04ea55f0c3114f2381633b68a4cb888f6025002a9b63fbd8ca7b66310cd02ed8891410caa8dc88d41e8a415ea07b502ae26dfc0f23274949b49c1a108d582929abe99d5e1ec1afd6081295aa95d863aec0dd1d441c652d147aa03b7778f86cabf4feff9f0dcab62f382da038d931565550a998a6300177ef16852ec8ea7177ae7c31f03dde27bb3ac04097e5deed75536048b982ffad0224862a797fa596abcc7aeab4cfb1128b4d299ba57569c14991d104dd80d47def6ce4920f6463a127c1fec07c957660411fe77659ed9909c18f214358649fb4b7a53f9aef191c6a619b72f36767aea866ca32fec9f7c3c4c0cfc29b9270301e43192de74b22312ce0307d1e779029dc0cc2641df177d65af20a81a9d4c164c9b9fe17623299ae9a439d24fb528ddaa813e1382f50aceaeba4f4e17b2f190875ed3169e8830ee9fd4e586d28b470a436c422f78f54b5e31c22eb71ba6ea5175e90b9e811afde1984fba4ba7fd7138d4f94110bc94f61ad6fd0ac301ff8759126112a8efe97ca7a74270f012745d95744e9b21e3e1114f142e7f4506c1d5b022a326603cb9e61d7ef9ca9af9eead0688cddf1f0e9e79a7c7ff7e7216de2d52d6366c50ea4d72980af729287299062bf016585a80b793c2a618829315b8d32e593cde087c0ebd0b3e2f68b60a77b62e4b6dfac551c1fddfca9d5654086007838dde5cc67af1cd5347b839fbc2a3b601a1428a3307a7b6c452d39f5f2efc48cd200c0ebb69b4a2fe4a0301996011766ff5f9e08084773653ab3c5a6d304646851f1229bce4441a26fbc763b1ba3d202678edc29558143d7d92f080fc2f293bf4261611dbd5d71579d50848f53ddf1ce4bcfb3778b15001c6e36eb67b27c366f944a46e338bcdcc1b22e8bbaf7a9ab749a716d1548281ad670be046a1117b9bcab3cf93eccc495c360c813ecdbc940e365c5d07c9c865d7994db72464134de8862e05a5aac658d1306115136929111056b513112b94dcd6913854e4302e9b8da0d5a3688e42a77d31049afd93edbd6f663585fae0ca9cfb17c5445c392c05e0fc2ac1a9098f615753142ed714a445676cf0fd387f94b28d1401969afd2e781916a46d55152898226d4cf8c0adda617f97a24a25c751a081a3ba3a3675239ac2cf89304c1fcdf7f25faad1b93e2fe5f9beaa884667928d7c972b99806016fa9b334ab9b1814ac2e603d04fa8130f226cfd31c5b5be32bb2ce6fdf306bd0aa47f55c31ecfc90c8eb69409924a34d436da7131224d8d59e87a60090ce419035ee22b050700bcaa8cc410c28ce2ed851bb391805619eceedd821072c46d2f0a561496d041a3dd0055a92cc70faa93429e96df9ca9ef2acab8cfac2f24d5df1a26615e44b4c6d68ba0a63515bb39a44c7eebeda53667c6856dde919c0cdce1282d60e540aa535277722c12e550f5dcb7c5a569b7aedad6aa81024ef29a2afc2244e8a0baf20d2b9808e1cfeed6c19ffa1f26cba3a7556f011856729b3148c41cc576ed547450b60364898b16b6f232578b071ea368e16d02c46140ebcdddaf095cd608d557752f0446befadee81920378d694d287f2cd975cba75edaaccd0c178043322281e63cc86addef17f93adf0142b745a191acffe2df9282fa8cc59e26cd11d3c234129d6e24ac7a80edc91f713456f42feee9c5dbbe58277a99dc960c43965ccf8f5f37600c7cd4e1e7287afc8b239a5d98ddfe797b62e0aad862ac1e5c12264b95bd1d2aa1034ca1b10d6505d7d3b031e472d87162656807ba94cc1fbfb73389e39a6670759bc9104769112e69888ed9492e5f0601ef4d327362f229eddb2160bacd58c824466a0e068df115e9e7a26ffbae354855b9bd12c4f5fd164e76f2cde40af0153429b6b93b4124c8515d2a0df018c362c82baf441fbb112b59b39e1ad440480ae845da5f0da7968b22812de3d55c505a66e83185232b611e5aefca50f49eb980bd09936da2408bc3ae21fc38b88335a942a1eac46be3d02e3357d06d2fea7d7646a63ac328be633834be65d0e7677c297fe6603415c2db023d6c7d93fad6323bac47959ee88cf9d7dd7afc975b4a6ebcfecf6d94f6dc0916fcaa73a9424f5938b3a78c88de738c6bd2d074f6fbc710a27522c7d072de3b08031966d19e3e6145ed03c8e7424398806d6be0745663c1eb569795a17397db18e99c9d753fe65c1fe100e7e578ce6d81b56c8641163def557c9de707bdf2ec109ce790c623693a7ac09bd00f138b54fb2c0ef9677b1301b9e943b3809602ca0df08e5947ab7b3dc7b92a07025b945524b9b60e13e22c85383b1ab647414ab520a9506c8021abd346b28bf084638e0cd5cd4c960a303848e7fb702941bd4074112323159a17e65f591c2c51d15013b311699085d29844e713e49d7ca3811be90b445407efb3f1491a9369be66f1733cca7dfcdda44e8456619db1b35c7fbbb5147a17e0dd516d24df54e0d2f30b0d0bb367027047b55b5cc6c545719c3d201a0ba0329ea2fecd891636ed7c518ae9a80be70a3e8d68a7b6dfb5bbdfce44df05271906694bef9991dd2b93771a8c7e56e49ff2e51daf9e0d3f5c57c04ca3b9d420572b59914480369b3533395ccc1d59dc5cf936157748635d0b0df287b396401f3febe2b66640467807b3a561d60a6f33ccd715c6ce2616d460e08e8c297f2115985256bc60ba9cf52fcd0e0f0006f483cc86d3d3054546ecf7515c9e2f22db0743bb98f49cea03b2d054e07f9387c2e2c491f60dde6124e4e653ffd01758fd1e90208914091428bc223f9bfb7d47769f54a10351825944e470a34452e4383e2b9d8cd677f4272bb6f301aff517ee19d8c6d26e50302c8ff881ee3c372b6a0210b261c12f780aee28170473022a0292ff986cff82f41c1bf6e75f6564d14acd03d82c0ae50678b7833c469454295cd16fa86a3d77b7d3d1bbaa592eae84ca3d1b0b97998674080de94b61bf612cf7a52460300b2bfbd8fe0be813e990c690a81f73672cdc59302ffd1769bef0539a0d8eaa2fda0a846f80d6537a8fac17d70b1321294f90ca6087d1acd15b62ebbc2b1980662376793723cbe7f66c7996385d0ba3ef57fa10cd3183b4ebed00456555a286fc6bb8a56c6f28fd23715d5d3e641436ced825920c5e35bbae4bc1ee184d4f4e1088fd4ed3442992b23549757fd41e41a9ee00a756d4cbc5093c733847c272fabbbb943bac49b7f67b94613f8aca2384c75ca769712a060b11dbe7fb946a62488777ddef3eb4d022ffeec19b1af115f096e92084cfbcdf2a87f2e6d694f48fd6b90421cacaceef6b0f065a9803ab272d9c7a25de1017cc54f1b85b02ae8f3683b10e9b76f119de5d5e440f5692e30ab6d935403785eda862e7c458b7fea8b5c840c0626614b8a8b4067b530534fa03b238061ba8657b39b434331be2302673f0ce60294ba481d77d8e727817a50119c299ce0bda3326499d53650ff0e6c91c67d0e4acdf376993c2fda330aa4c0815cdbab2c0f39a306d810d2740ca6235b2aecd44aafe2d19af35ed2e91821d750cda6efe3750af7fc6e9489718eafcfa890e46ac80530a09548a9f868c1de68d0777958e2823f42220d0c7335bb6530ce1c9b1d0fee4eed8722c1c274b34b733167e0068c567ef19ed10074eeaaf32868a0db6339aa06f7a4775389630ca541a0ad631de05794ab4b435d7a559c9c15a70ab7b749408887c2946febfbbbe94ad0e0564652c81da25c99012864db017181b0a354eb669e8cddb80a4faeacda61202161a23dda5f03090d98f2e1ac41e1744bd54e742930626a3282801296273b704198d9fe2e77ae7f27ecfd799ab594500a7f06fd7bfc12df12dd7346ecd2cec1e179b0e3d0df234478fd57da5186662715a06215ea1cb9a51c8c7cf764ff1370fcb71c852ca352e691914f882bf7ee433aba9b9330dc788eb0cc3bc186dc4970644530cae4fe9eb4378ffc5bdeac5553003b488b3b104c54d291a6a02aa740d169a8edddb15fe361b37ecb7e6b656e89318cc4d6179f12ce5d83b462fb147fe0fa8dc0d4712694f99a0fffd3e7f5b26a022bc5a6187dee65d95fdcf53a5c9ca2f8b6e698e1e40e5ea563f87d14a1239cd00f98e211f0d4366c399b019fb0d370862d7f7123d0f3e5d7444f8307ee9f572840500405335cc5caf32186534769724315d39be30984007825f0f39aa8aef7162d5c71fd92206526c0f9281b045e30872d7863e767a51baf7528ccdc40b3b7ddc901272aaf49e8db445745e091d521dae2a349eeceb8ec2005f354d3f7baa8e241dce5810f73b52ac51a493fd577209f0bc1f8f06b64eafd62cc335ab483f1cc360f4b1fa696040ede6f2e45072be0db1f34fe0ad83c4f68e6b370d178b3e423025fa00d566ecedcfebd7eeb03e4e1bde0a6a0f2e2274a02f53f98bda1bb371a31ae52a4ed4113b33536cd0c682061d8b41c9623bf740d5c9542de5bec1cc026bcd0fe3f13c04fb30d54bea7125210a1837fb2a45570cab37d71f08016cf5c4d068cdd5dfea9633e0ac24157a428f1d1a6cebb0fecd4a9e7beff3435ac114b0c1116c1dd93b5cd09e7d4677403dad1f0744a62811431d79faad9982487c683a569cbac706d7ab92e34c95ebfe7d161a726e7458d7fc3db95918c3e10fee2f27229cfa45a72b2f784aac5379fd74282ea92b48b37c426ff65b4ccb37b22d94bfcc3533e8e31cb17a6205affb577b58201d8b85d80404b34d56a77e70c80cb71825a1a778fc2ba73eaddd095f2a4e951c1c05b28c934c28a081775b1997a68f21341fb519de93f6173dcd61a81952d22ca3454699b888d7dced53518c2685ee2b41f732207d2e55bba6a3f06200ac5809f3b8e35d9492d38b35c6cf7f85d684e43a27ba99f6f4ce0ca03503fcc40ebc113288f7db53d33d2d482071bfce317782e9645c80e799b7e8fc6316c6f967040ef9466f2819c51aab13c380c59c053e9446a1028af2ff140a45ab1d100e349b1edf43d1e608258ab24a036acbe98c5ca2cfca7b16427a0a8d92fe2bc724c2a117df8c5884b8dce4e8c72261f6b9938fbfb5eeb6f934381cc418f6bafcf8fe3e1df5ebb5478bed6764b2dcd8fc4c1637c5a4c652dd58f70e886298f034bee54520854cb53c29d5c2486d9185af7d0c4152475ed2573cc42918327229c33ccf8f24dcde531eba48b772467c065d349b529d64be896641535f5599160515f4f65d088ece267511953dbec37ab611110c0a02fcb3bfe3c5748a4c98e600a17cfc7f10d556cddc99ea9dff8534d94bb47fb8a85b914a3af2f4bfad5eb674ec20312e19a85a9306294efaac2e2fdb21820ea1f3c2559c02263cc169bb598f34d18b81142f5f2c0def1eb95325c656a88d2837206e7aa3c425fe6237b5b9ebe9f451dd1cc8d19a07a47c81be7aae09274f815990821e3c78cac3a0601017a4c15a42fe2d0b165d7512e418a00fb57e026cbde7c44f658781f7a9e9c2e7da5cd5ee7d1816e212d7bbf79c15bd799fb57016904b3875c8bde238f4920aabbb76edc82cb42b75b86a1ea2b7ee9080c5a0e97692ab61b23f07a7a7c09401c5485dda082c59169f741f1e2f44631b2d80ca48b06a5253484f3cabfb5f607c77f0826907d9d52b6e7529bd165edeea3b36818eaa0060296a398fa46ac4b136d9d5a29f8e7f60106337b94b2c90432384b20809c8a9005e1697464be3e2ab781aaba43adca93f208fa31ad74c3a86beda603e27f7b45150462c1bb6dca0c6fdf284839f9f3651183b01559bf3832552bc672425e5fe95033e8ed38eaa570a16833a49cd91935e21a1ee9762f06f4a404a16365419615996bd31c236f93c133bb8bf389d59165425f44ab020f37d31fc08ee6d5012697363a55e232c7f1d14d690cf207fbc5da309136b84a66d5e28abda47856ad76934986b0b5cc117943a650db5ecf6001c5b0dc21cd10696ece41b9a5627dce92f10f68f69ebbb67068bcfee4016d19b17a119cc81a517120663c27a93dbd67b613455e938161bf474cbe45e5b3d491b60a3175cb45c100cbb4a3163e18690166a70286371e26fdfc3bbcac5415023a3e437115cc35d9144e8a7971f22d083435bf2d49d7c8fd8d4363313041484c5b6c6d30305ae16940272795adc186de9034872b98c2efad741eb649e5912c6eb24023e2b38073d9a3a16045fcef840f36113013ecbdb4e31d382162e3373422d04598511595178e82b2f4e5072b4583cf147d64a7cdaa3b569f952fa1e055b09e567a32c5861ae96b79ddc406d8c438de191930afbc11eaf0b207954e5e517461f6fbd2d9a7250213742520756bc0455a7fae69af6365ec24e38d9d218e49d96e5dc8e2c5e5afeeac1c6599386ef05ce9ecbf1f345e8d4bcbae174e17baa4b10685f111c2c9bb4b551af806a0878c7729b1778eebe2ce15f556a14a9dbce2edace73fc1cbbcc885f665ddd48e23441a93d13db980ae95c657aae496e2b6034636385c201758d600a911420f217c44cecd2316379aef9da72d5032bf4b0f5257d13a18421fdb18fe6db460aeb07c5966715ce036a298a9cab35d9b43cad7ed5461a43a71077e028cd20efff2051f734e275204014bc23a0671ed712345f176ceb999e1125d58eed0c5eca7ebd3619708151920fc43446dba90f2f88247db17fb4b64edc1ca88d2380d3ecd9f7efef0298007d6960581855b932d35a4213928d508e77f829fdc3bda81856e981ef206847d2e8085458e91d13d6dce26d96aabda4e9f9810067f8dd7c4f06dcca9f95a2323235341616980069d11121bbcd33550279e3901b1d419ca0817dc7e807427ee29a2dc91b6c7f6e4eb4a1a762a307646f41d1d11312cefd3724e2b7156d53f69cac49d3ae19f65f68afe0aa6092d7386f1cbdae2b3ba163ee69c10ec3faa564808adfe260e8df21ef6e7f9ad57c8b805520e76b2826b35879dd321583e8cffc4131b94ba9c4d231f8f995594efb9e527cc7f9012bdaffef70dc799cbed3d4940efb82b22514a4d5698c6cfdf7b3662a1e5421092e804225c58a77d7b5680dbfcb9f9df2ade756badd51b7559b98525aa54d4c3318b3c4d5560837a3a729a31ee310bd6207631f5df9bf3e8a6218d352751bbf7a250f0c5a646fc90263ab7968d9fbb6ef483641167c8b0eba1377191e20093e2e08d6177beedf571ad42bffdcf96fb9f40501f5b84a78960b66985df6f9c057a41e9fd4da23baa7b909d5aae3538de740b2a4c8ec6584fe41e631ca83b86706530b578962823e8643199dcd9b41819644cc30135e1e818c4bce1f57de86b397a5156c7553d625f36903c845d863611600f84d78277e3d9790605e8cf2cadd71691729717335c736aa6a9153b6ef712cac2b82d5c2a7b715f768806e65ffae5aba240604ee50d572eb6ed4d07fbade6c4b32c277a4f69405043529f0beccf243b72074b14364416e5973d7e42583a68524e044afd37e9ca1aa1a29b76c990e7f5c1fd6d7575a0e41d547663d7cdfbb830cf27b2cb11fdfca499609fc911f2d3fdf1c0e6ee6ce979f7c549f2b0feffa37d663f01f1f809d2e46abb7fedf664dc0c4043ba054f3faf9e871c76e9cecc8e7434c365404b4b8bb414cab36bc9b7e24c40eeba3060ffc01be48087ee7f2fb4d843f2fb45c0b5c74a1e76fb2bd1b617d7b0d7c329cc7067c00b4da2143ca6c27a1b2b539007cc5ab3807e9515930daf5092ed74873359ed5fd89a3b4c5de7a6c9a43577925c920fb0fbee7ea3ef9e96fc426804da3e6d126bab1208bb546038d090b0132981f367100dcfb0a115c75eee7092f5a8c776c075b6351c568fcbce310d093eac86cc32dc95dedfb3662f97f0d0387c492a639c2b52fcc1cf99da62bf36b08e5bc35e9b6366a208a654b1a30640e4c335a59c9ec05d18df44d34f5ed370da8b8f0d2ab0f2374532d2b4d46e2612b3c3bae59697c2b5aea7e9b009f2b00951916aac7ca643abdc5754bf730368803257f051be59dd6d8254d72c26ed8f3a42080d025c9eb3af7cafc720d6f51f4250db73b27940954ff31120b47964a30fd00e16de908631f420758372025fbc21e38ad3f651f77e0584d079988fba4375b48de91a4d75a72a8c78b852cc847be0445783486e4cb50e5b85553d0fb63215451a9048de4f1a2087fdfcb8d07f9002abe6c018421e696859aa05c7016d974c0e0e85f2525399a0edaab9bbf8a46a407e8d1d4502b787bc23a7314259d0ace3713c2ae0a78e369c102b86f9f36d8411f5046937b2bf1f1cad431270589bacff484b44aa83c94e55c6d350ae9395cbf1e37d06d364b07c2d7f961bbd43de17ef977e7b6a229c9600b6205a1f9f249ad02a3dfde19ec25c2aa9765e94d13caecd230e580ef300c96ec44c639652f2a65169297f49c5be4633470ce866727e481ae8bf475265b1f952fd3e64e3025618e0b48da97f0ce76544cc4e612b311015134ad07bfd2f01f0e7b0a1cbf55ce13ae0032323a7954ad174d2428a1b223939427d479902a237f1ed5cbabf1efc9ed3e1d8c46fcee1f17ac272b613341201a440a26aef199517feb1aeb8ee3b4c3718f1fdb0a453a99daef7589ab06fa32e8c759638f52cd6bd729d238d3ef03d2d23d0537c2f246edca8e01c4368f885c7d098d13059dbded59987cd139baf6692e8f1f70a4f865196a6ef4f9a78710e7b5e4dc8926712b6c13b3d7c6a8b575627c5312bb48fd8ec7c9d942c79e222f04dcaf35265f3b5c8c68d5839aa30dac5776962c73669bfcbbcc715636c059f19e7b9a56fdc0eeac9243251b18400be5b8219b1d0711db43dfa2de7c8a7ae15306976fa9e54a4e48af3d2070a6128b45d0ac14217a55d88aea1a541a38291fc8768325805e31b0cd90e854eea3c08de49ae9be26f6ca50d10587edbbe5237f503a200b02e3d8bf97941a463b4c807e87df81fc2a0e3db9c7becfc264e70c61ae05782c1743e4b62aaeed19c5c5c6c36ea5a8a112ef0619b7561dd8ad7dbf428231303dccd9f849d5871a2e9c48eeed6708f21e20472a162e8c02718790d4505666091fe541f53f33420043e4246e8684c5ab03146995c0de1d706e6c447a3e4f31ad14d466c86aa237e5141759e2112e47f9db2c1930894f7ffcf965f7c5b049106df1fd68b7a78b75e8eb39875d98d9a2e3181c1c4aef24e43a1711f983880bdc0e75b227c17803c9769dcd80440761253aa22f3f9ecc853b8ee6d1a712a2996f7f8c8b13595de7726d7cede0cbc6241620f2292084c96a93a786dd4d6d068581949e42a75c6d50dbafc1832e2e53b37b6e2302e6e76dbe91f82a68d8c1af36433b6d107e330ce64f099aeb0a9278cdd19532114a11142e94c83ea7399592308a43f458611297c378b58ed93a8a2083f292ec038daea837196ddbf670f167890e789206a7776f2fd92d7cd5dbd85b0a36228209cc11a51adf334a65e1e7ccf864ea627f7d2910315ac276b476efa4dd2c17f92041e624375b9b4a6822eb10dc30c7e63b8bc16b51d5c41c608a91714284a6c6133924beeb4462b7427107acde8d959af73d9cc6ab5c7fc24e782b39410899942176786f3239ee9161003c53fef0ff27499c0fdade027ac7e86a8b855dc0efee2b0d4b51bd77959a1e0cd4846b3c9fb73c40c41c1ab9479cdc876ecce5eeed3b0088513aa8537c8877794dcf3cad741c620d8965003ae8c6f5f9023ea5d888780b08afed968167a16b4d353e9268030d73f508befad879ee4ff791dbbd92ec66124dbd712a5d154fcf6c725a5e4f2096041596e279bcb0c2769e1f1d0839533a0411f20ac41148eb634d5717392ca0affae5bc908fc38e9b22bbfce94cf010c710a3b90f7b52e1ec48e89ac290e0d93dd773e29b2968130972dc3ed95ca345c61c1b04c2f811ec823da7994dde140019fac4a3ef85df611c110c237c7fcb67022789f78bdc3a97e530d2539e7ddf0b5d1044e9aab21ef58a7c85617c54177b581183064dd7e9e49eac9fc81ec3aef666708508e1104e9f3b1e7aed4ae07c8ba5071cc1af751014c5bf9e9c30a8d3ed99cb9bbd5a50035f3f344a66b2fe15f496208c7d156dbf7f929986f94b5bd5332869c5ef65ab4bf4a2ef8e86463ec5b6b6057dc06923ea3d5bc147abc2647cfe7528d657090a2e961b6a48e64cb62e56f304f12820ad6108fca5a528c1e811a12316cb349ae4592de4de806b194b085123061b22a5e185a5832aa9ea54256f537a6aa2883381022ef32699d20159d9f53d22eb7f86fe46d6ba98480bf41a9f7c0b9e5b371e85970f288ef55206ac8eae2216f7099290a045298e4330d7d1a8ff1f47383f71cc2361e5f51a4dc25fff951e1a7760e0062f6dd4e989334ae081c07ce1e0b74d8a7a7436186f28ea99a070b819ec1d2076c9d799f625e69973abcf2f628bf1e7acb8c59f812ff826acb107f2289471e7b908827a3c6731495688bb6b92d68e3b3816466b65694f5befe7ce71284a1aa2aee0cc81bf1d8045829d978bde2e6a828a02e262194f6549b3e5b6bf0f61110c827c620ddaf9bc971d8afa0be1f15af22ff3f22352802850fad58b9d29092a5d945bd68c7398c080caad8280bef41034d84dedf9bdbf7cf0de38526c186b3f9a5fd07ce6356d2baf2bf1b9635879e61cb9ccbf3775c97c28f8148a0f251b28fab4fc402761be25433aad18b73c35b839472d173da86506b4c2e60f7e1b7b4911cc03f7a161764cb25ca58b238a13afb49672bc996f240aa95486472f0e8455e8d028bb6a39d7f021f2c8e4bb38fced4d7bee1afcf8dcb8fa321499ba16671b76dea745ebb1a759ac0421c243fefc78c6e312d1d70588b0ef05e138782873323ed10ab8d5ca109f0bcd8a7e6cc1dd41a0ef22938eea18a0884a75916f2411e8552727fd0d669defd942bf948f7f78fda74ad3ab014bc21e987fd89c0f02701ce9cbbbece457abcbf25d284ae27ec3a378923e182d4d175cb923ced7b50f2302552a944503674e0fa87e28f45070e275d5320c2f3d23bdfd5ad9048fba083aa2e42c7b24549d4d36e2fe8741a1e6528c5ed4e75cf2a170914c181949d614d64c25267b569c992022b998280b1e5ee72c0779578eb2da5b8b485b3d406c1f0a02c9de1896e85ac5b10fc9e878cf42d86e9607c93fafeb66f6135bcb18332947238aa807a50b9afe7423ecdb65ab85f05494862b572c835ae1f5989315f70174c2fde60f5f69b5a3ba28044d3f719737b29b5c4c479aea59b55fdba84399167e1264a2ca15d68b7054fcdae548de6d141239e96ff593aeff299171346b7b2f8a746a57b471a0b5468100825870533d1ff5690c3a35917b009d7acb0b910f0eb080bf486bb09661378c9af42d8ed3b541ce12c3eab09390347db7637f4e00010facebdb3973ca5cb2c2b1eab263e8c934886c7bcb041e9b3f6d1473b7fb2d03612e8108986804004366d8c81de6ae5e3f2ab2a5e875cf2d9fb1fd0aa60b491cf46d69f552ceb8bc3edd44f7782665af728ed70248f4d7a47f31c661a4aec0184f40de72482ed8294defbb6f0e27f03e7cf2717137f7747c5a985a6d97758d1b5615444c4ac911bff57a15164c8e1411ef3baf99c3cc00a525e6bfd878302f2362fc38e16b7eb6d7b1081b7321cceaea15dabe7af98dceddd4db928239c90123fb16abfca7b9dbd319fcf54a828bd1f89a4edff60fa3d027ddea22f6b8cc082bd0588ccb42c5bdfe0e2de6105c92eb41dfa53ea3e644fa43c66710233429272f04be3d84767cac1a318739ac4771cfa1b8c7857bf0f302806b94f7bcd6a0a02c2c7b9ee9f7ad1a4ad3edf98f7edac4fa84022b5a0cadccbb598a7828c600fb055721c86fdfc4f1836cc81614fa2faa8e3b7658912e860e7ab1eb9e1d79572710b501d8ec29001fe59e91c398b3351e93b68ed300a9513bb9e76fe582388a010d82c42dc111fcfb6b428985c6add8c22b8e2eaa12d83b8b06ed74e4a96a908f251cd86488618b0adb1e40a01835af12200706112bd58da81d1a2012eb2054632b6684dbe5e31054b578775c49bf3820cb022513d611675c940008445fed94f61754bd324aa37a484a0f4f51be92d44d94e6bd47dd1f29452f0149ae1164c57d0cd4e189d2a995cbf93f371bab53232cbc8d99caba2bd108e869b66191089ccc09f74c712e337c9959eedd69584ffcc90bdc11f6d0aee4c8a96b03921c78a8bb1f78d7f70fd72a759ae09c54f6234f86a75d6de285f1b07370bfb66d1ffa97602999b9eb40b834ea307430ea7fb49497a83a375d6006c2a3c5be21b4393563d5268c99d7e8b70e036909f8596cb636f2bc05a86367ed39ac198d2ffb190259ca2b17c86c57831633f304d7216f80b2664f35e3276e76f39b1739c8993c0ec28b119c87e837d61a1e4ebc7095a3a3d522e969276959de691df84e1553b9d5f7b827135b07eedb11acead94ed5cda852fed50bd963686d42a9c31498b2fbcc804729b08b24aabcdc54028d958c66451e1d289c47043a8603908c57f117cac8f27f121368624fbcf0546e6238d7248a29e0a3b5ff2c6ff182871a9e7b7585a4c04e13253cec272cc65d5bf2b4c864bdb43bba0b83b1ea4eff53b5f1f449a9f85d7f27e64532d38ba28e829852e8fdd4cddcd8ddbf5a9c0c1c738a17b5600cf7070c22d99d8b95d617d444871d91c26f6047a1e9f98d590e193c8bb16887c3f987a10df34a36ccd6de19633479b2d7a0c74b080ba140bd477c2f0e88348c9b4497da2d464ff71dab0d3816ca8666644550bac74327cabcf9c762a7f934705957b13b2b42d06efa1d7aea4ff8d73b62a5c2fe26f1bded97a6d259d7cae5a4aefba06e1e19a258408a34ebb634bb89d79cfd227c490c31c4137b23721a14a085eb33e82061f8d36c1411fb2b9e94983dece932fa45f21ce3fef99b90306506f3b0efe22fe59c183c1e3fb5b2b48ec745d88808813c771a0e2779d128b0c4e80975e56c0e40e42deb6d5d026c15f48c5ac93ee8f353d966989fa1147c9550b08b0d971b2296b11070bca184bfbdf1c728d7d24dbc018eafaf1ba85a597a3affffe8c62f29aff586eaa75743a836430b57d7d81f08b3d8bfaa441289af8262f7d7f0771928f4261d643c71d5060ce9ded17000cb5f6f0e4d5c7be470226b0ddf5931e81905ad5ff8bde94bb880ef047a90262492748d4e2059a2611b2dd2465a6559843e0ae25f9a29a9aa4999fa291a1ed4c080f552f55156a3204b3c8189bdda58e8271f3ff9b466a525ee596de8881bd5a927e341bc2cea65de5ecbc44e7a8ff967fe04618944b73761bc9ca9e7c92c96b3f6d4d6afd92a952390f5c2bb853b5cec5910cd646288a39dc3132a06de5d58477c752c9dd55c522f42885e9031f44033d30865cb14c0acd736ac88b1e1a2578f9b2da6e44a1a5112a3fe90a68a9360d3420cffe8acaf2cce9c167da8925b76db9ed11ee21f2a59dfc20a24856a80ff40ac0366ceece8d72a9d021d91b00b8047ebfd7c1c457058e23088060e3d0e83a6661f14f572fab9520b079fb7263b3e944733ed7069263ffc8803fda4988ebb26a62d47654257ff30bd5ead9d2b90e3ef340d7179cbbd312a1ce6a3db19e84c84082794b0c26d58a78dad48bb4ee28fb566a91bc9bae147df3540d294f499256db40bd79c6f3ebd88d0f328e266214e96a37ccd37eadc62bbd70388417764f04ff32f21f80b5f6989fe6346bf964198320ecf03683eb51cc190d1b99774e3d9d2b20f04827a91aa0c1c1f16d8d390e13f85cb7f2207b57961eb42942227da99f52f02a3919711cdde39d33ad27d5e4bd2e9bb3c954d8591ba210e8074386c79d64e0518fc2dcfb254e4f0477b0f28b44c88fd4367b5d5cc130803b13056cb1f5859", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441156615ed204aa948509f830e7e8756e609e419f8a6f8561fddd9202f8abcba0100000000000000000000000000000000e40957462902168235249211dd6ba6ee00000000000000000000000000000000f8defc3ae63e7ea8b9be28634acc72d901cad4adce90c01d548eb5a88e3935668204c5bddb827b618a40626d8bc1281f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002069abb3ab89cdd02776ca33f3ca6e0d10986b7d8cb986d0f7483b530eb616eaf1ff0f767423d4fb0ac0d17aeed5ea083d9f2bcc677df5fffd068a9470bb0650a003f9b2ed5d6ab5749b0ece4b4aa05407fa4ad7f08b38aed610a3106e903bd182a2e15e0c5d25a298fc02ff12751572e031a6b9c5317a80e14909d881df864a5000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000a6a7b38193b9791f900000000000000000000000000000000000000000000000938adfed18caae40000000000000000000000000000000000000000000000000e21f5e071314e88420000000000000000000000000000000000000000000000000001dcc3d8161f47000000000000000000000000000000000000000000000002e46c7ed7ae9e45950000000000000000000000000000000000000000000000006b452c19c9c3a95200000000000000000000000000000000000000000000000f937207f5e9d7f27200000000000000000000000000000000000000000000000000011234ce13989700000000000000000000000000000000000000000000000bc917c80a1df8a9de000000000000000000000000000000000000000000000004158d1dc6c5fe0793000000000000000000000000000000000000000000000004df57309713331b9200000000000000000000000000000000000000000000000000007698a06035d700000000000000000000000000000000000000000000000b5363c615bf3049790000000000000000000000000000000000000000000000084836ce0c2626f54700000000000000000000000000000000000000000000000de7f5f0f37ffeb22c0000000000000000000000000000000000000000000000000001bca3c66a8927213f200493e130f2f241df6f50e717977080ee976b404ea52ce9a1739a6538942186c4ce5d408bc4469b54741acb17f81532bb104e167f13788ca258657e19ad18bdeaeabf611459f9516a80aa25dae22a716359feb8721e6f73ce82c65ed90504097a64060c9b98a318d0db5087034f8fb8be44fa54632799b74281b02fa3201b5874dca01aa6c15157bbcebfe698062bc743018cbf4c1838fd88dbc7725f1e2cc9baf93dc954f7630b60d425d36ef81eb1be7d8436127e0444eeea525630a31058031e3a95f311de89e96eed8d8edd125cde39fe180ff58ece8d79e1d4b19f2d6403f1677a3e0f98f7d8669a44ee9d1c5227bc6dc8f7fab18b5ea38a23a8a909d6b647f178c12bf5765e4f3fd9e9353f1578914f6289ab3626968195e2204a0b75d68cafcb752224084dafa2b4ac94248907a3cf380066780a921c968cac8c1745343af4f66eb1673679b9c00300c26624904c97f542932abd632e9d9c20b613f65677439219bc9826056b26cd285c1f6ad8cf48940c417ae82c3b6dda3ef601d957cb411fdb0e5a236ac0758e0f106e5c2aea4d997e4e082051f36f37a07e193e06fcb0040e75764cfe7d7b19379e865f44cde8c4021fe7a2cecf7276e482125b04ce7829f541ae0f786317799e17a5d19b18c35032546d862d824f01aa2b2ec87c35f2af31bba08cf65076c41c5b2a532fce570414bfc6619bde14f1f90519c766248124f4e391908fe40e4f4c2e47d959872db00ccc6c66104be6e3a4a01509488b91bdae4548075814bb3cda9893c4acada580aa2c29959515a57de7a31cc4f2c4335d29fdf50154c3993f6ae9031d06f0541f0e718afaf53d83631efd232f3df1898a0799134d1f263b0bfd6dccf19c72088d8811c5e361ba2efb54be181628501e2f6edc49dd9c2140408a2fa36d107456bc9ca11d66c1e426b5c3300cd87deaf468a61d8cd99c96247bf074714476fdffb493aaaf976d4dd235e99b154e79b2ff5e50ab7f8c6ed0796649601b9c5b5efbb5fc5f5b4f216469ba83161e04612689209e87667d0a819663d69a261da2a789ec0318543355b471178ee51394b1536f2652a7fd5ae364f01c53350a79a0a2c030636f3c2b0b1a4f11f8f70b7dd2a2ab687739334dfa9d83fc290414ae7a1369b7b416834d3971e6e4d10c1e739ee4f5603547ba8ba72746f349ac646d2ab09e4faa5ab7e68fd55609c6fe055052cb8a99d490c94cdd0e20fda8125a7fc867867440b977632d440bc72ddf2a79774bb4346a8defd31709e0d3be983bce16ec2f1b6eb0d064df61d33fe4eb0c0a9a31f221442947a6eb27a710db271537ecb73fcaa4ddc4435b85207362ff13ebd5a7cea692e2c11170b8f0e19c3996c9d4fe03f579bee10705ba2cb6a64120e3ec3fd8a3623c614a7233e19fd586b3dcddb5ed038a7b87085bc0d01f35ac0a910d04789ce430f092f249b8a95e3f3e9e57d464b7e39b11b0b2ce0511a53f06732a3e7d8ef4a98f1c0197fe86514ff68a832af68a50812a33c5b1b3d7357f2f0fcac439af16b2c3c51ccc318db8bfedef50919ca1df0ecef4cc3a1295d63a16e24b4427e5397d2aadf907e4021dc59573232a6851a4772bd0d7a9511e5e940c45c13faf58cea6eeddbb0a5bab2b269c8d205cd66a41192cb1a02c691451c60cd43ed39782c235f5b7222db00e52277c1bde022d68a3dbef6836899945390002967f653a03db955a5c8ce104f9bded599366c20d9e4e10815d1676c7de75591876c3b768ca0c50d93bbf03fbebd73298b550e6731cc9df74ce6b043d391fe501dcac5bfba80aea4039869994a026ae689f9fd7bbc45b9da35927b07a1400b8050faed5f52a92daf28e2f7a1e113694c5751fa2819d425c7038381219f561d31ab37ed6c11585362715d889de16fa9c110bafe87a89fb3437d64c75602257e3111a8aae71085083c00061af68a50a444442c811ca0702b1ba59efa80707a12e0574243f7c35ad11d0499bf94e73ddb1a0551fd5dc7137b31c2cb4ebc6845b5c11c03ef720b358f0b3417c3073e3d486c03aebb7b020db1ea2ebd23e08aa22bd2657df69bdd20d3023feef2bd7b0efde8763523aae1b31d1bebc86a1577122722af7b9eb04d62bb5d2dd340cb78eaedc412be64449d0314cfaf30771d59b4913156f4b94b14bf9ce19924e16e516ff1bc66e6955224645525d4eb36650422744014aeed42b2f48def893f4b59685a7a00fd42ae1856463b2ee65aa8f09c21def2f6d4a920159b6487b8928519e70a5adaf40e615c04e7b549c276109ed3888e808c5f4a847d257ea6478a544aab60e354c2cd9251e4456ba225db340d3c35b841ab4876ba9361592e0e2f351f1cef7a81e5875c24f4deb7bd8720b702b4d02941825cdc9b2fd2f8aa1e13b760f9e5f6b87dc45dd74defd1cb013a8894bfd211818a2926b858bb7f334bded04473e5c398fe186bbfa744b7c667066c72db7d9a32d8649606e140be47dbd386381b1fbb1a6238a8c38d9346a624d27d057ca8ef71e68e91fdcd8345fca7dfa89faeebce29c454669b769baaaba0fc712dc541ef8280fe2e79458e28de1f0312d7a3dde0572fe68bbf86f046faa8c2e07927b080f28ec7285880a3c089913308ed80d9c983352b34317079dc36f024c88cbe698f1199da4d50f7601e1a92ce7e958b907c3d08ec1c966d10f838864b06cb903a26f183dff4905cb4bd6861c48fc2e538b3fa22385197567809a7167ec25251e71811b35520a843903bea42f31b157ee343aa5335adec9bcd3120a6d1e4edb158539292537016521f1e1719774bc3472bf8cea73aec615aac019b47e7d879642f462263b44a7a876820f0117f968f8b0bcd1478699159b190360eb331181a2c04be40bb9562ccf1693476d7beea398beed58780d39123f31c1c401b3e59502899dcb070a2498fdee2da8d6266a91ed4ff61d016eaac361e643de776750e0b5e0675313820988967945cf62b69b46bb5aa6914de5556c053dd102f5065ef07c881fcc2858d7069c1c6f7915d99118c1d498f3232b2fbe53f19ae68faf49837c761efd1110cb2769cf48c492062503b64b03bb339905c4871ba162089ecb07e88f922b23a72ef676abd989368ca5ca6efaca4a3546caffe57f71aa15550539f41efe8523d17d0a9e67ea0a1f0e79adda4f7729011eeba0d13f56e2e03db3aa4ea0f1b62ac55a22371b0798281faa7e0ebffa3f57ef9db292b0a30d64528ff64c693dda2a3524a0f84f2bb33871e969321e34c0af763e3076e7f460e92e6cc346eb967e087fe895a6acbbca4f7dd6b474ce3421a5c24f07129f17b389059945b911b318113acae2204d6152a672ffe6be7699486fb5feb674a9fa1d63454eb801e0b0071570f519abe7972bf439e8cf28cce87ba571e359329415b61dfd9605d2e5ee440603623c89bda7c629b4a2dba72f964bf3fb0a45d364198059f2bd2b10ffa4bb0a422889998c100754be16415e5a16450ec06bad480226610e78379eb5be4c2f04a91545a58c5916584ce39798d9f1dcf7742481e3fc90226b8a508e45a3179b207ca949a9e68369a4e7a0e259d4706e083f51f4172b24d00940a5e40c5041721c4e2ba1ee11f1bcf9c2ba0876dcd5bc3b74e779fe4fb8318c7406d47ba5b7da0d76ef4979ba783d83ebf8c7d834f6b0e244320263afd46395579b9a73d00a400646144e651a725f8d554dd3612862c70c98fac4a6dc4e7648ae1c99498221430bb83c7a9e1667315b09ff19d292693ca49c81463918d0c8f89e56e7f1cb588f12100b03aae55f7fbf686c6d8fcdfd2ec95fbde59c33ef6e9162c39088034e3427bfb892d72474b03a197db7242e68fce96b631564d2f94eed762c1c722e38040e22909f91577a6ec1e2c330e5a47c8bb5e6ef018f1a83388c73eb754ff32cfb0c4d38ba8b27885ddadf3f520c796d6fc499fcbc2a3c5a83b97cb77a5174d60d2c1234826405656d94521ee61b88a233af7754bd82be24ee44bef1648438d4fe075b2767664640965dd21e2f989bbf4f2f69c5b1d0d1ca61c90f1ccf62a2f766251a5ee4b01447113c913372d0d49d6415b7a479dc7ef064c184b9140d8bee6a0c1089dd993d092a9e654da9b9d13748d9ff552c1d284a9543151223275818ac1bd3d204262544f9b536b40ac64089e0ffe99370db2f5a5303b14f7f1b43feb901c4dbe086dbbdb2f83982b5a415dbd75c8eb212760574f15b9065a39654e17829f0ed29bce220f0eb2ba77754697a8babd74c5dc104896f4bef310d99a77532076a118a429672a66d8dd32e72a259623f4a330291c23a0b39b4376abd57c6b92ef5e2faffdcb6fdf0419185e0528d35366a1b865dc91dc4a1e228379b219eb52a47d3160a98e6bef560957039f8687bff28bbd1654b0e1a2290f335c53f0e0016dcb8f0a0c2e0d7bbccd7415da0fd8725055b26393b87f071918eda8414cbfc13d7d1c5beffe174876dd3ddae96fa6cdf87acad3ba1709dea8c80f4c7d2e4bf0e894e78471fa89f727f99c2073b57ad0ab2b23ab429995f9eb947fdb176e28c2ff432eba5830407d95888ac9843beee5f7c224ecc1a40afaf7534b5c3010aa00d026e74c47892b4c803ccba015b1341b380f9b441eb79f26c0b1c8447d23f912dfb9d2fe28a5cefa496ff3dbde6fc9fa6767339c8035ca97086d6aadb88b0dc1321688e887d98b25cd01819655e9b1432200c665a0a21d40e7e0244f1c373a202aecf5ee088442b4fd2c5313001a02f584d75eb7b6e52776bcfe474f016bd692a2062da16361e2626c90db4aee731753bac3d68a2963f02fab8f43d4fbf52461e1737999975f52f29533837f361e89088c3be55ca975278143f7bc986da19f71c483e99e599ac5ec9c532fbd2c9dab0e088b6978ed7e65dc278a736e5d03792040ddeb18cea5bb841cca78a2f449e0b360ab2ce1f1dd4c8cb39ea12679615111308ccef9632654f64e19d97906c95bebb106b2700a865a5165b9b8ff470a3b72fb6a7f3a91f58d47828e155cc94dcdd10ad68ba207859b696ccd40783fd81f814647a611ec4c8370bf8c01b1ecc618a945f5432f2981b1c4b34c75077d83d252d91e6be4bafe7db360df60b17cdc52871f581610fd0a35442b466230b85830d294f38eee8d3f464f1191bf6f3697840817fab5682593db32bd18c3e35c279a623cd32c6a3098f4ebd0c1b8944809175090b273ca8fcfc6b2381835cfef50ad7135204c539b5ef18e1256f2107d81daa0665d4e3bf06f0ecfc0a1cf514d0aab105a9eb9f695af73b3ba0df3cb2d1c46b7c02bf108567e6d01cf49e6d767d078d0f7eb40d460e34d78692e0114c3f260886fcc7e00c8b3c7008ff429d1a53aa9c2e2a41d1cb34e154057c4686b4ad1d704d5fdca331c9f8168d21ee75a74a465224f3327845653d52575e720a797cda03fef68668ce7623ef7cc9034bee94b8b0181f2b11ce4e8710e7497246d272de79509f4e2fe6c8b42422f866c5c69f6fd41d41c9c326912a2fc919382d4e3d414ef9fc9ddf546ab52c8d93afc585b420390737f26228865603a7ae33fc8e19c944994028eb2a20b57bfe1a2f15f0f372fd14ef2c297e78695908b0d6cedf144040f14908d59b7ffa58b1b1930bd65270942bd8043e38037bb5213ccccb00db3df8a661e6fd31d165ae465810350f8182320df72bd876d111321de34fe704b96862982f670d697336dcd782eee7b90ebac50dd31b62a91af03d377532a157b4262a7d1cba57fa045d75bda66dfc194c0b8d09ef91d887469dafcae262b0ce0c2d8876e189885ee793cc622d42f5719247341ca89e1cdd4026584e5087558fa70f750cb3e58f15f4655cb2aa5c2072ab82f11dd0b47a22d769197a373b5a799afb067f275a5477a2592432165fbe5e5fe93416f0816a98599b5cdb93b09175bb78f688bfa15eef313efb9d49047188736c9f096d86d047d951f18de0d5bb2b435d9cfaa88521fa55553ac1171aa3676e41ef1387921e9df4c7c6e996e54033a9115e1159e9cedc6121ec9e81a1f516dcde852f17605c68e6a41ce312832b731dee504377e669986a4438985af9523357a9f41ab6b9d08bd5e05aa971a009802ce32e31c29228a1f314a2da664067dae295662923d7715c95afb00e860d12d7b168ae050cf29e4bf1ca4fd61b9129794383fc25db22ad3229fe1d00f4761e37e897caa298944185e893a6bceeb54e22b467102f2d8e3e1b2aa2658687b4f52d165f059f4053284f6f3797ed016a1cd51d6ccd1920f19badf516351bb00b5ac3bf3f00da9600778734dfe59ff118b80c46662518e010c4e1220cc9a38032e0cfabd5a5a5da75a1a827179c567443a9f95c14710c03c74cfaf3ba71459a2e6c84aaa0c8dee4d69514795029ea15228a4696961201437035923818557408cdb632f2206a5d9b67d82fb00e9c7c62a3a47c330f5f0875884e7471a7c2cf9b2ed4d9573c90e0ddd52b583e9ef1b23884e89a0c0c801599f00ff79b96f561378e455584cde3a3f5a4c84a86cb62c0998f4f6beba9ff1d9cc5d092a623abf9cd026ca12b2c816c0a63ba7666422d2609f8896501f94116a4ffe86b44b683ba8071cdd9b7db1f7aa2a76e30c1607e8a6aa03a5384ed982b6b6e809ead534b62fd2963a7027800b08e53c94f16a1b00b753322db877ab20fd39eb60b9280409a4e963c6e5ef6f66e7f8713a7690762ad71158a9c3698d42fce32038142ee603c46b0d6510f279e56c2b05d7096c2416cc3dfba58fdc0c50a8b5d89048bef8ad00d7b6fc744234baefdbc29faf5e4694f0d0bc87ea2397a0de33752094611077ec049e3deae0062a3d54d9c55d2986f356ee54d7ec2645c18012c3dfccee4fed0bbf219d4cdd93b94c2f1dcdd964a7ccba4954033a07815248455ea2bc5b096f90cb81d4b534a82a8c55f8df025430884c395c2992128871e87c67a24158e7d9f2e700b685d9450e5084256931384cb517ce80ecbd955671b13c2855124079a3f8a423b00e0512ba3ee812ac85cc596ff81ccfa7c31dea015cfeea3704b8289741c066bf18d60a8f6e453921513d10a7494b7a8ed8bf4430e906fe2dca5b1f2daf50b5151e78872f7f971fde706bfc267af4d6c71c43c461b331c16f81c053dfc33bbe0973f91e979d96622bb2805eca8bd1ca1c86a7e4f2222e04ad38f0cee9892e7f9aec41ea9fa52334b246d476203f7c2d5f6575b7d126f845c8396208049ac014e067a49c7da865ae3e17c4b4630e63630abc4958e1924a21d32c26166f01ae2b8da72d7a56e1afa6621287663eb5d2983187964f81753ae5213daad6800140e34f3af2d7e227cd2c5bd9d4758586f6a4d60cd0cc70034d64e1fb4d2ab6ed9be9b2810571f2189149d9dfd08f1f8abe05475d3f445230413f3c2d8d7a32ff4c27a59f3922a9523e81ceb9a2b0a35482808b08de05f2686e7dd5fd0b5cf19516cb0558a76d4d97770e28404a2e78dcb63f3b96eb19f1bbf291e88a6145eb27e6fa5ed3609e829094f9cf937c58dd2bbeac045e27afa19407b425abe489f434d671692197d5cf72c07c8e2ac447b2db81b8e0e248d621f0521aeedf472184eff3527a64c13f0c601b3ec2048af2672255c14010fb1430fdd8b36f426c6469ce3c6b0be2cac2ae4ceea8baadd17bd0900a75114a14a370e499163de5a0e817ec703ec5b636140892fa8203d20c032cbd574fc34d2385320f8ef889048179132035bf18bc196f5b08bc5e4d0ae4bf18768342d5b48f90d27c5eda7c200bb025a2de774ef9a8dfd0d75fd844969950a13f5b4320a250d4a124fd0beae2313ac372d0583b79543c07fe4a31afc144acf99238c31b3e0b9c6241225467d7ef6a633313309fa84669b737cda0632d2dd36ec901e1ae3e4a68210fa0b0fa02588eff6e72f73f015951e42f85868c622a8be41f3247a2140f3fc0a30d31f2379fd387240e5f306a17b035720f57dba876e1cd0d33bc87d966d620e65736370fc817ac4ff15db12147cc85c230ebef736047408e03ca8f0be6a642030b5627458aa1e65dd56737ab810b2a8b492586c42e193ce28d9aae97a13db0cc36d358f6efdfc379ee394140a6700fd99e86b8f7d3a594cceb4bbefff68ce15866cdfc9894097f08e89b66f8c30bffcc001044b0594922e11b8a055c9f6ee026ae31ac020370a1f3e06daa5a772272941a101c11714fe8d8e37d40c3122c61d8f610d194cf82da8419cfa1680eb14ee96b6686e9f209a00df36e96a9d6f842571b23c753c5577e9ed466bb585e8910722050a93a8463bb655ccac5d9900a31a460738ff88a624304a1e5b37fb9ffb23887000c006db10e2b4b8985770d88102ff55d36e7513f37acb4c5920fbd220fc20c8b9b1a83128b0c64bcf26f761bc01407ff23a9b11942b6034278ae1b4817169180989eef86eba3f781c6ed7c82b279089823170533c0791d57ca17f1beb9bd6fda0242993bf5be154fa2377340d0c0c8fcd0da22a25000793deaaf634421431ac4d940c6ccbe9265f52eafaa6660cffe9ce6f0ae13c38cb7ce5304e697ff3814a992c030ff909d9b33615c8f9be2f199990be4b96c115324f1f53c609d9c70c9a3d643973be4f407aa54cfcb7741124c48570a6a3906b2eaa017d1c4e98b1336932976e7ac4b2cc3ff548a3d5272ca9b66092c0be62c903cc5f6022fe892c37fbaaa5bba75f41e40b410b223b052ed39b580fcc936b9582cf7c479ce148f56a374df66ddb07dd6547ed67da09e72e7820676bd33cc3dcc778b5e5929677908c6f43d3f2e793b768cb2bb6c11cac258438c7630aa10ec88ac11e66c0855e0be63beebbc4a846a4210d3b16a1a45b07144aa8b8587d6d981683640bcdb7390599cc6b806869d01e425cc59f78d26e009412daa789b0872610de5d576f928321c80f83d64dd5cac4562cfbd72b56bd17165c7c60feb78c973289e4afbb58225262f916b2e189f9195aaaaef30f037d1a0b47a5e1ad425f16c581f4317b4a1cce0458eb64e01a2ce88067b6365f20570e7a6ef374c26e7df9d9b86f1a2ed3f3a6f151d108f24245346f7fbacc41e39d1b89bbc83ce93442bd733e4fb70c090135e71518d07f6f7cb5484256d973b5e107cadc46b3caecb4a0c85930d59631ae69c164b9b51d4af12b414da442ca6e2e2e905b3d35c848bc74803d93cdd1a52642f101ecb76adfbb324aab602cda06f4142f88ea187aa2d9bc2770fb55738bf1c22a0172dd3aaaf0756da7919ea8fd480bd1852cd97487ce18b5406f1e208103de94a56189a902b0e677d3ebb09b9b4403e78dbd0aa42f5e177f3c0e44bff9304af9cfc15d61d2b7172aaff7db8e31c401014a1d5637a540050a7c24f8ce92509be63c99f33c3e169be4e48c5c46c0d00536d73fdfaacd543fc05f62e194e64df3916521560c2e7c70651b0c8cd1711e13cf10ed6d7db7642d832dac4b6699b200a8bae01e5c271a7cb1dd55be7593252ec16d138fea76b7ab76eb2597f4847ffa61f1e5560f85d5b1dfb976384bd384137d0d599a996035b696364b2bbd910add9dd520e1d50d4acbaa28865a9eceb70caf6cfd4e46b787fcef171be5316b41061ad0f844d78c571948c0c3b9ca5b581e66da9006818668cb3024873fb6c209e388964e71e4e7481f60e98293614a491c4343db492b43eaf5acc1b24720b645f73a741b8082ec8e7f0940270bd337222e0e5f38ae454a0bbbb97e2684eb4c0f8665e2f5fb567a7fc8f46d42d76a87b9167d5caaa34cac8c6c35e92f339daa280b074923950ca4fbe26d12fc08df611b122b294ee8165d619809c84a8483ac031a91a321ba578cfff08ff1da3638da881a658ee5025ca8cd4c2412bd64fec5acce4a063c903a928a1bbe47a19d6f20ce2a45e48b775e956e7328e2807bdf0699717624dcc4bea3f49696076d6bd1f3b32648c84b627afc98ee7a255a9b6a7688bc2cb1d022b456c914d73c31a61cdf1f0990d46e50809e625fee7f66f13f3fd3d711053420474e2137299fb499c55d280fadb264bfa1c01fb103c85e1025144416fb45c410b1de94ce3b6b39e31dbdd40085c18596d5a4f3aa5d72f5d762f32657466c3d6cfa119e4b15d66c166f9235130b6b5db7bdeb688674c2c9ffe5953f913dc8b70c89ee3a3e9d0b900d3eb11b0c998e9028b8af68a3a123f0a5aa56f987cb287bed1b4ec7ea4d5080f7460191140fbbc521c4b4ca284d2f52ba94d12c57cffdf6b3a1823c8bec3f227f0964d0269567969b2a6952d67b2cf3fe4ed64ad32021de8df302d6fa0e5934dcb0fe0207c4eaf81924185e0ae50fa740e900c098f17483a8c8e9acbd142b0d3b8d7ed31222b7d9d9642133a4d710850b29be1b312f2043e0553468a03a468da16119a0147bc6655daaeeaccef77df33300f0304b229d0a67f2e2686ed0faf697a8c0be02a92c5eecec61f86730c46d3e4060cedea01e6ad69edab787224daf217fc00d25bc51eaadc89bea9ed8b8b064d00a603bba923afd50feeaa28480fe0bf5208e1f6c3c1ab06c7581870abe1d4263967bca1c83e6d829d9967b15b03c71662b051fef32423b6f60529ef22b34e79fd39a03188920fb378c832b94ae04ee9c1e611c19e994f3fa9a123454d8453ff1ab96e3dfaf32adcce9ce3a58d3ab46800283017c41df169d9030faa8635070293a6b4f92c2a03a23f322f9219eea5c13a65e16f8cfbb4c12898a9b5666c005ee1bfaa0d64396e1ffb0c89394ac75ce404e341592c70ec8ef696ac200a491dc290b060b0d8ab0b0d73a785117cb992fd5082920edf8a802617e40021efd904982120cb763677cfbf5fb26e040250ff71a9e8f0a74f8c8c0cbb2d83b70592dcad430759e5e3365da322b45198c34067f1ef09108461a7aff586e522eff5dd06ba39f32adbbb2ac11cb5ed218b080ef6b47ab0a21d7658d2c57a0b782547f8c5acce7c340cbc4e32291a4f4d261d51eb5d19fc313f13da04d155cee22bdda2a05328dfad96d058d2752e41372178a8ac3069d27277f5ad615312c8a0da2d6f5967a6191fdfa66a465ba370e3d8a8b27f31b75cc1d1dfc9e914df09e8044c0d753950f5a662bdec0dc6b50c3fac197df947b2fe927b02824b638eba85cc6b3a3a4633ea595e9e7c01764122a2a188c7f05b86a0901781fe9cf000e194128b474d8334de7204e7cf9ba4c45a82851cc873cb2d851257d3c067a363b22d3ffc4592989e1cdd50333e7d20858eaded8fa02b85209c329cb4ba3f99922719ac16e761fee300db4c4022070de18149af7d30df3be006208f5703c677000acfdbdab00aa099e0c515cdd6c36e4c2fcb14d4a169ac5226206d780418161648be96d949099a7cd18b491d72cb00baf6a36552c49578cb858004493fa5ffabec5316a34f8af4e3e329c28ace755c8329add179dd15a716fc30cae70fd816fb9ae67f0e595a39585efe683dac6569cecd5b4612600ca7e58142774ac04ad50ebf3fd3ffbe32cfdacc60c4942daa5b1dbd52cb4570ce3acffe92365dd10cb6526daa7ba1a0b8635167f7220c97ce29b11fff0e2780c3d43d82700b6511664384bafbc8ace53cb17529a7cbe4fa1e5b8242a149af5120092d5f50af044e091387602eb00690a21ed41b2b6cd2ea58ffd23c273f07932cea2d0e81664d0b1770c1a8726cbf09e109bb62cf0bbd05bd068eeb95e2f120470b4082e0f037bed194d5aaedd71fdb36b71ebabfc29ba3fc10086cf780c1cdc74ebe0250fa517cbde50830063cefcc39e7848ae2f4ca899903296fefa908e8c18e1bb8a1437ac612ce96cf6c0617a6cd6c6e8a631315a1d3e29d803b558c3a4274960d005b212c9c31e65649812c0dc066ae224f5001a4893fdad31cf49271b8df0353c2feb0c6796198d5ddff04951416833ce4200813f0f39d2ce75da2985d4329c1f2b2023d34a62804670f522d449b1edbf12c255673077c821683b1d8d7e5ddf800a4b7b539cba5dcaf1fdb336daec57ba5cc8e484c04b5e70f74ee3a0a2e954e2175117e97ac2c5bdc2dd375946a0355561c2cb8edb457d5185a93189e13a4aea066c51edf64ec0704d127db99b2ce23ca840435bc4ce9d8b533e23bd09c80f5004b23d8666c77a078368ad040c4e1ce3d4fddf9ed1b1863c9ed1d2eea60ed85f24edbcea0b513edb75f6321e428db3b3082154a7923b2776b96f9276f251b155188c9415d7e4f09e37f89fbaddef8410979efa36c6a1efe5565349a96498aea518ac498adad8a301e7d171c35883c89798ca890032253d77b00393525dfc1c992b73dcd9fdf3f304f8b5c1f7a71f4036d4095ffca43ba1c2ed25761063f0ee3925fcb36659ca28e692b6f5e89599405f990c57ec7237311d663bdafde7d2f81d0dcc1c522e2497eb105f9d9bb4f969cb1c89c2c4010dead0a19023f4bf15879c2f13afbd1caf80f6e3b5628c1e36f57189ec2f5a61d03eaa58e379df3027da6f24403ef8d186c62c5176573cdb2e799c65bb6c0da4fcaba21d4b8c02afeb6b8717fe4b74f45e45025aaacd2ee55901d81ab63d04853f222695e0969e3a9f89b9297aa44364a7a2bcd0be5759f3a27bc00d7a99f32b69cce48cbba3cc858a9fc708deb0b9515755e48edb793a598de521bfecb827ca3fe1e58b9875426b76a268137ba4e5fe24a5d7f977fe9d1b50ce573fe98be883ce057ff046e26d999403f52ff08e8a7dfb7f993a389212ea2ed1bf7725f4a265bdbfc208fcda3df33465c90928f1e1bb77db1955ef73583031893947c902eecff3fe24903319d5c2433e0b0546305540bd96a71fda765fcad36af678a4f07cc95fcd02e81ac2760931d40c132c998a8c0b120abd2d3e666518d1ef5ffa130cca13b78e57cad7a151bb594a1e394e73d71aea3c2d6e5cbbff872c98714fb5fb571e1e3743eea704b295666d21ce4e84ca11a1d673a27b169c5f83088e0ed344fd75dd4b741b05226ab93513305f4a7f83ded532c62d697e04ba2a963e30c321f916c0080e9fbc5ab619aa8c220f38de69ea6d5a49d99f904ace4f9c6132a8708e225fa2ab197c4508ff25c42a92d2a7cc3855cd59f6c96407266922cfb6f523d46e47e29d0447f32582e1951a6b0361e26226f8379a50f2d5e3204c6828da40b8e7cdc816f6b166ef4aa723077f7be16b732f1edab20a5668e50844b717dacc74dd94451cb9460cca88ad8a22ff47083c488ffc04e3b9c39ec7b25f41ee2a2a6cae755bc5d1fb9e1633549013968d569bb6c6f0d95547d93eba03a1da40766f3ab3b18f19b30964af1d69db15b13c2024eb855dd70af579f46fad36a77a77c2cf65812e92125f2f9e4011eb2ccf4716ddca655668da9838e29677662101824afbe875bd3490fa1a7a286ae31704b40587eec531af8bf89a26692686387927daec1d6e004fe9105160219a7d23035a362fa532f57b6269292058de35d8cd36c4c6b1143821adbe16379d11e1232646f4d497651f0954ea9a1df894dfc491003980f60b52cc0a1ab26756077227ee3102ea57dce3271965fbc8bd6e66a68a2b9543b577278024cd4536b2741226c3b1d409828f4e0d9874ecd7069da3c9c77f290915adcdf6b456983796b3fc28042c760483b93e07a9ac85a3991713d1c2f85b94556c6d1a273da1ee90265d01a8981245c7f7f8d10b2915126d9f9ac61a2d6a8c055ea87be9bafcb5643c9b119120d465b5ddb005f7ccba998fd30058ffa33d82c9d8e4e301e780bd7f93832604cfea9de23f1d6b2a50f65047ac9f879eff0451f311164941d680b76395aa208016ec3c5dfd1e4e2451ec13fb58f2242b1c26e54bbd88b44e163e4e7c7aad0b3d5897b5f0d8d49522bd7b5c1aeabc1faa5f92051b7c9609bf3dabb085a533130bb3f740556f8e71453758b79613a124d615ab01b2e1de8a4e519947fcd62b059aca90452fd3b086e6f37ec6b9c8c565885d7bc2125f2a6edc128992c986ed1e16fd93e88a03407f497ac742f49836ac0886ff6354b8a0d7096dd464cbbd9e1182fbc13662c46028cf859e93de493b32a8060828596b2fc4245655a4f9589c0c3e6a3130d7703084bca793e15a9e9d6366f1898695e5d47b7da2fe660461450c47a7186e240f9f600739c2fa2038f28aa34075563b42e39189fff917f77b1d169abf6085508100e18e9651f68113f0371c993ba004d5ac582763655c4df79c28668406aa60c8966a70355446b5ef97eca8f9c21cd311efbd65d6701932662d214ba804cb02a5d0c4e936c50435a6d8a2678558c26aedd38694ee726b9fd89a039a0378b17166714bb1379bb173d2e916411278d8285e9e6dc901a616904b1927280ff562b631da5d3cc81ff70e3269a358e8c4dca3696ccdb546ae1147014621b472511388d61cb1e60590ad87f05617f122417a4282d94bbc643f0547a6682008695507d53505be0bd9b78ed3c7adb01360df7cc3b4e8ac5357485facb49c2e0600e9a923c71d12fcaffcddc8de640403018a9a2ff1c2ebcdfd6700facbbb2dbc42db37fa4e0b64fa45559ed421cf9e91bbb8533983393113a75023ebc8090bb50f8d0b75903c3c8e4b51137f3ccf2b5ee42e549fa6c2c57a336718e1867420e656c75e985702a0b1d78e451a4d0e7abfe681ecfd92e1fe5d00c00a22acdd0ff415858ee7fa9d195fbd1071d672106427bcd2c8f5114e6b4e3b8f6fba28c62b38318cbd8088a2b956ddcdebbcfe0e6670d37b63276fa30a16791a3fee1b9d", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e4411dfdc7cb6265f100524012f038ee1f205bf8789b70b46c57463dedb4998f7ac800000000000000000000000000000000c6895e67e144556602cbe13943c8169600000000000000000000000000000000808b3aef60f3e74a629e9331d68a14b21806572c19ee2f3532e970d617919b3aa8cdc582782dfad0699badc631f75128000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022c17de150116bdca2a3edbbac4f77678c67c35f056f9e969387b99b5c26da55509a23e762ede2ab19d289955f6fc34a289c4196b6f4fed818bca760158098e851b1e385ae72fb3add3fe838e63d0e080c2409bd2fe5f57f926a01a4e31278a1e0fd8fef8ee3dacae5dd4bd8666f29088e7e2c1680f50451cf263d3e53ba2b8b4000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 835cadfae..3b92a5ae6 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -25,6 +25,12 @@ chains: deploy_block: 51 program: dev: true +# Default profile: multithread uses (logical CPUs - 1) Rayon workers and the same concurrent job +# limit. Reserve threads with `multithread_reserve_threads` (default 1 for Actix / libp2p). +# Example override on a 16-core host: +# node: +# multithread_reserve_threads: 1 +# multithread_concurrent_jobs: 8 nodes: cn1: address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index 81bd43da3..e93ecf649 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -21,9 +21,6 @@ chains: fee_token: address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" deploy_block: 7 - dkg_fold_attestation_verifier: - address: "0x9d4454B023096f34B160D6B654540c56A1F81688" - deploy_block: 32 program: dev: true From 604ef9af71651ffae19546146b78a7940744741f Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sat, 23 May 2026 13:24:09 +0200 Subject: [PATCH 41/54] lint --- packages/enclave-contracts/test/Enclave.spec.ts | 4 ---- .../test/Pricing/PullPaymentsAndAllowlist.spec.ts | 5 ----- .../enclave-contracts/test/Slashing/SlashingManager.spec.ts | 4 +--- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 231dbd7d2..684b7a0d6 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -8,15 +8,11 @@ import type { Signer } from "ethers"; import { ADDRESS_TWO as AddressTwo, - DATA as data, deployEnclaveSystem, - encodeMockDkgProof, - BFV_PARAMS_DEFAULT as encodedE3ProgramParams, ENCRYPTION_SCHEME_ID as encryptionSchemeId, ethers, makeRequest, networkHelpers, - PROOF as proof, setupAndPublishCommittee, DEFAULT_TIMEOUT_CONFIG as timeoutConfig, } from "./fixtures"; diff --git a/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts b/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts index 159288578..ee7001c80 100644 --- a/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts +++ b/packages/enclave-contracts/test/Pricing/PullPaymentsAndAllowlist.spec.ts @@ -182,11 +182,6 @@ describe("Enclave — pull payments + fee-token allow-list", function () { }; await enclave.request(req2); const e3Id2 = 1; - const nodes = [ - await ctx.operator1.getAddress(), - await ctx.operator2.getAddress(), - await ctx.operator3.getAddress(), - ]; await setupAndPublishCommittee( ctx.ciphernodeRegistryContract, e3Id2, diff --git a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts index 72caf4289..4461d2754 100644 --- a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts +++ b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts @@ -629,9 +629,7 @@ describe("SlashingManager", function () { const signatures: string[] = []; // `dataHash` must equal `keccak256(evidence)` per the on-chain check. - const evidence = "0x"; - const evidenceHash = ethers.keccak256(evidence); - + // This case signs `ZeroHash` deliberately (mismatched vs real evidence). for (let i = 0; i < sortedVoters.length; i++) { const voterAddr = sortedVoters[i]; voters.push(voterAddr); From a96c4209e78a2ad3ccc0a3a4fe7e21d4f3dcc12d Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sat, 23 May 2026 18:23:47 +0200 Subject: [PATCH 42/54] benches & runs --- circuits/.gitignore | 4 +- circuits/benchmarks/README.md | 25 +-- .../benchmark_run_meta.json | 2 +- .../results_secure_agg/crisp_verify_gas.json | 98 +++++------ .../integration_summary.json | 128 +++++++-------- .../benchmarks/results_secure_agg/report.md | 154 +++++++++--------- .../crisp_verify_gas.json | 56 +++---- .../integration_summary.json | 84 +++++----- .../results_secure_no_agg/report.md | 38 ++--- .../scripts/check_circuit_preset_artifacts.sh | 11 ++ .../scripts/extract_crisp_verify_gas.sh | 19 +-- .../scripts/replay_folded_verify_gas.sh | 9 +- circuits/benchmarks/scripts/run_benchmarks.sh | 1 - packages/enclave-contracts/.gitignore | 3 + .../scripts/benchmarkGasFromRaw.ts | 143 ++++++++++++---- scripts/build-circuits.ts | 116 ++++++++++++- scripts/generate-verifiers.ts | 129 +++++++++++---- 17 files changed, 636 insertions(+), 384 deletions(-) diff --git a/circuits/.gitignore b/circuits/.gitignore index 68eca2b53..5c2ed3bb0 100644 --- a/circuits/.gitignore +++ b/circuits/.gitignore @@ -1,2 +1,4 @@ target/ -Prover.toml \ No newline at end of file +Prover.toml +# Which BFV preset last populated circuits/bin/target/ (written by build-circuits hydrate/build). +.active-preset.json \ No newline at end of file diff --git a/circuits/benchmarks/README.md b/circuits/benchmarks/README.md index d5d65b41c..5fe45048e 100644 --- a/circuits/benchmarks/README.md +++ b/circuits/benchmarks/README.md @@ -179,18 +179,19 @@ EVM verifier `estimateGas` in `packages/enclave-contracts/scripts/benchmarkGasFr `extract_crisp_verify_gas.sh` (and `replay_folded_verify_gas.sh --build `) call `ensure_circuit_preset_built.sh`, which runs -`pnpm build:circuits --skip-if-built --no-clean --no-clean-targets` by default (skips recompile when -`dist/circuits//.build-stamp.json` and marker artifacts match the current circuit sources). -Then `pnpm generate:verifiers --check --no-compile` **verifies** the committed Honk Solidity -verifiers (`DkgAggregatorVerifier.sol`, `DecryptionAggregatorVerifier.sol`) match the current -circuits' recursive VKs. Benchmarks do **not** rewrite the committed verifiers — drift fails the run -loudly. The committed verifiers are pinned to the **`insecure-512`** preset, so `--check` only runs -after `dist/circuits/insecure-512/.build-stamp.json` confirms that preset was last built; the secure -benchmark path (`--mode secure --build secure-8192`) builds and proves against `secure-8192` -artifacts but does not regenerate or check the committed `.sol` files — those are locked to -`insecure-512` (see [`scripts/README.md`](../../scripts/README.md#verifier-generator)). If you see -`❌ Solidity verifier(s) drift from current circuit VKs` or -`❌ Canonical preset 'insecure-512' is not built`, follow the fix recipe printed by the script. +`pnpm build:circuits --skip-if-built --no-clean --no-clean-targets` by default. When +`dist/circuits//` is already built but `circuits/bin/` still reflects another preset (e.g. +you ran insecure benchmarks after a secure build), the build script **hydrates** `circuits/bin` from +`dist/` in seconds instead of recompiling (~50 minutes for `secure-8192`). You only pay the full +compile once per preset until circuit sources change. Then +`pnpm generate:verifiers --check --no-compile --preset ` verifies that +`dist/circuits//` is built and `circuits/bin/.active-preset.json` matches the benchmark mode +(`insecure-512` for `--mode insecure`, `secure-8192` for `--mode secure`). For **insecure** runs it +also diffs the committed Honk Solidity verifiers (`DkgAggregatorVerifier.sol`, +`DecryptionAggregatorVerifier.sol`) against the current insecure VKs. For **secure** runs it skips +that `.sol` diff (committed verifiers stay pinned to `insecure-512` for production deploy); gas +replay deploys fresh aggregator verifiers from `circuits/bin` at runtime. If you see preset mismatch +or insecure drift errors, follow the fix recipe printed by the script. - **`--force-build`** on extract/replay/ensure: full rebuild (same as a fresh `build:circuits`). - **`--skip-build`** on extract/replay: skip circuit build and Honk generation (only re-run diff --git a/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json b/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json index d847b317d..efd38bac6 100644 --- a/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json +++ b/circuits/benchmarks/results_secure_agg/benchmark_run_meta.json @@ -3,7 +3,7 @@ "bfv_preset_subdir": "secure-8192", "proof_aggregation": true, "multithread_jobs": 13, - "verbose": false, + "verbose": true, "nodes_spawned": 20, "committee_size_n": 3, "network_model": "in_process_bus", diff --git a/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json b/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json index 945d39c28..c758c6a15 100644 --- a/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json +++ b/circuits/benchmarks/results_secure_agg/crisp_verify_gas.json @@ -1,8 +1,8 @@ { "verify_gas": { - "dkg": 3042585, - "user": 2972941, - "dec": 3553811 + "dkg": 3125282, + "user": 2973001, + "dec": 3641070 }, "source": "folded_proof_export_plus_crisp_verify_test", "artifact_sizes_bytes": { @@ -17,14 +17,14 @@ }, "calldata_gas": { "dkg": { - "proof": 170076, - "public_inputs": 6156, - "total": 176232 + "proof": 169992, + "public_inputs": 6144, + "total": 176136 }, "dec": { - "proof": 170076, - "public_inputs": 17316, - "total": 187392 + "proof": 170052, + "public_inputs": 17292, + "total": 187344 } }, "integration_summary": { @@ -47,55 +47,55 @@ "dkg_fold_attestation_verifier": "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.612713375, "runs": 3, "total_seconds": 1.838140126 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 2.183483124, "runs": 3, "total_seconds": 6.550449374 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 1.943779167, "runs": 1, "total_seconds": 1.943779167 }, - { "name": "GenEsiSss", "avg_seconds": 0.804676139, "runs": 3, "total_seconds": 2.414028417 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 1.468442666, "runs": 3, "total_seconds": 4.405328 }, - { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 7.19246175, "runs": 3, "total_seconds": 21.577385251 }, - { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 51.025217958, "runs": 3, "total_seconds": 153.075653875 }, - { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 6.830379416, "runs": 3, "total_seconds": 20.49113825 }, - { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 55.386840874, "runs": 3, "total_seconds": 166.160522624 }, - { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 8.455168041, "runs": 3, "total_seconds": 25.365504124 }, - { "name": "NodeDkgFold/node_fold", "avg_seconds": 16.317384, "runs": 3, "total_seconds": 48.952152 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 19.080945417, "runs": 1, "total_seconds": 19.080945417 }, - { "name": "ZkDecryptionAggregation", "avg_seconds": 50.556414917, "runs": 1, "total_seconds": 50.556414917 }, - { "name": "ZkDkgAggregation", "avg_seconds": 21.026504167, "runs": 1, "total_seconds": 21.026504167 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 52.175752756, "runs": 6, "total_seconds": 313.054516541 }, - { "name": "ZkNodeDkgFold", "avg_seconds": 145.216893416, "runs": 3, "total_seconds": 435.65068025 }, - { "name": "ZkPkAggregation", "avg_seconds": 105.563541209, "runs": 1, "total_seconds": 105.563541209 }, - { "name": "ZkPkBfv", "avg_seconds": 5.966218292, "runs": 3, "total_seconds": 17.898654876 }, - { "name": "ZkPkGeneration", "avg_seconds": 379.488959389, "runs": 3, "total_seconds": 1138.466878167 }, - { "name": "ZkShareComputation", "avg_seconds": 101.477507611, "runs": 6, "total_seconds": 608.865045668 }, - { "name": "ZkShareEncryption", "avg_seconds": 288.672301658, "runs": 36, "total_seconds": 10392.202859709 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 305.775330152, "runs": 3, "total_seconds": 917.325990458 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.114692638, "runs": 3, "total_seconds": 0.344077916 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.34096735, "runs": 5, "total_seconds": 1.704836751 } + { "name": "CalculateDecryptionKey", "avg_seconds": 0.036223306, "runs": 3, "total_seconds": 0.108669918 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.156799111, "runs": 3, "total_seconds": 0.470397334 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.234711584, "runs": 1, "total_seconds": 0.234711584 }, + { "name": "GenEsiSss", "avg_seconds": 0.220404125, "runs": 3, "total_seconds": 0.661212376 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.202163278, "runs": 3, "total_seconds": 0.606489834 }, + { "name": "NodeDkgFold/c2ab_fold", "avg_seconds": 7.089881625, "runs": 3, "total_seconds": 21.269644876 }, + { "name": "NodeDkgFold/c3a_fold", "avg_seconds": 57.963919402, "runs": 3, "total_seconds": 173.891758208 }, + { "name": "NodeDkgFold/c3ab_fold", "avg_seconds": 6.906346736, "runs": 3, "total_seconds": 20.719040208 }, + { "name": "NodeDkgFold/c3b_fold", "avg_seconds": 50.328348527, "runs": 3, "total_seconds": 150.985045583 }, + { "name": "NodeDkgFold/c4ab_fold", "avg_seconds": 8.440406458, "runs": 3, "total_seconds": 25.321219375 }, + { "name": "NodeDkgFold/node_fold", "avg_seconds": 15.28584993, "runs": 3, "total_seconds": 45.857549792 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 2.887353333, "runs": 1, "total_seconds": 2.887353333 }, + { "name": "ZkDecryptionAggregation", "avg_seconds": 47.438696625, "runs": 1, "total_seconds": 47.438696625 }, + { "name": "ZkDkgAggregation", "avg_seconds": 19.61153325, "runs": 1, "total_seconds": 19.61153325 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 22.058676235, "runs": 6, "total_seconds": 132.352057415 }, + { "name": "ZkNodeDkgFold", "avg_seconds": 146.017955028, "runs": 3, "total_seconds": 438.053865084 }, + { "name": "ZkPkAggregation", "avg_seconds": 24.53227325, "runs": 1, "total_seconds": 24.53227325 }, + { "name": "ZkPkBfv", "avg_seconds": 3.559760472, "runs": 3, "total_seconds": 10.679281417 }, + { "name": "ZkPkGeneration", "avg_seconds": 70.937173541, "runs": 3, "total_seconds": 212.811520625 }, + { "name": "ZkShareComputation", "avg_seconds": 36.93532184, "runs": 6, "total_seconds": 221.611931041 }, + { "name": "ZkShareEncryption", "avg_seconds": 116.868327526, "runs": 36, "total_seconds": 4207.259790957 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 106.505539597, "runs": 3, "total_seconds": 319.516618791 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.12560836, "runs": 3, "total_seconds": 0.376825082 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.320742583, "runs": 5, "total_seconds": 1.603712916 } ], - "operation_timings_total_seconds": 14474.515027254, + "operation_timings_total_seconds": 6078.861198874, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, - { "label": "Setup completed", "seconds": 3.246619875, "metric": "wall_clock" }, - { "label": "Committee Setup Completed", "seconds": 20.239166083, "metric": "wall_clock" }, - { "label": "Committee Finalization Complete", "seconds": 0.004966542, "metric": "wall_clock" }, - { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 157.906869, "metric": "wall_clock" }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 1350.540072166, "metric": "wall_clock" }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 1357.853179958, "metric": "wall_clock" }, - { "label": "Application CT Gen", "seconds": 7.889468042, "metric": "wall_clock" }, - { "label": "Running FHE Application", "seconds": 0.0710175, "metric": "wall_clock" }, - { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 70.07983, "metric": "wall_clock" }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 389.344160916, "metric": "wall_clock" }, - { "label": "Entire Test", "seconds": 1778.650297542, "metric": "wall_clock" } + { "label": "Setup completed", "seconds": 2.661124958, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.166480416, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.0012645, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 162.089313, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 604.932522792, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 605.453073833, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.353204792, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.000809292, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 50.493282, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 159.9251575, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 788.561521291, "metric": "wall_clock" } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000be4031062af5469ba000000000000000000000000000000000000000000000000c2b34be503df778500000000000000000000000000000000000000000000000d7e8ac169d44cd819000000000000000000000000000000000000000000000000000226e9f937dc8400000000000000000000000000000000000000000000000eb2c7b5a6fbbfe202000000000000000000000000000000000000000000000007e63ce51619b2b03a00000000000000000000000000000000000000000000000cd2bc066f5bfb7eb0000000000000000000000000000000000000000000000000000180a275d95616000000000000000000000000000000000000000000000001588f71ba712f91cf00000000000000000000000000000000000000000000000f5e274bc9e3112d9700000000000000000000000000000000000000000000000ce2900e6f8a2b3ea70000000000000000000000000000000000000000000000000001d450a0feb34a00000000000000000000000000000000000000000000000893eef95a46af9b6700000000000000000000000000000000000000000000000610ea1690987e94780000000000000000000000000000000000000000000000026773ee1ccae29e350000000000000000000000000000000000000000000000000000f7de5dd648ef04d5fc40bfcb431dfd8b2fe77b6946e66eb7a8691ab31b9ad300d29366030dc721ad9f08fd9c1205bb5a208829de6f760b8cfa7311a3d4e9012cc8357b8d6cb9099a4a7906202912663409413b8f95db686885add4bec427fda8a078948d4d5628aa9359e286cfd9e938616af59b08fe7e0ddb1670ede6046a1c0c58e62575dd064b6290f765e9ffe9947c9677e07f369d130a04e51b428ebe31ee0728043fbd0202992904bb4d2cb55f641534bdab7fbf513e59fc0fd094a50116faf966cd71266144d996fb80a1d67ebfc31c554736779b58eabe68a9b77e288d8de098bb6825d67e8f8af229c06cda9a9e53c6dbdb50d895d4a469a46daa56f6b37a7895131925d6f35fcc62ab791e2d95d20ec944f9f2a744da733c1d2ff4ec7742d7ccb809c38965d7317c3eae99030b673c6b7311bd610393138b0b54fdacef3f7dcbf41d5858342b0f9a0496cf06e00856a1c613c46164b150eac569b07e31bdb1f5c50292ba9b7110cc200cba66dacb3700ca2b93cdfbcd99547807cad9a6778416780c0b613383c0e7b7687df425fbeee700961f6e6f8f1cd8edbe2ded521cadd5972e2b63224fd73010db4b74ed0f3c7fbec0e1c78c7c30ae38e3fc7d58c8096e64048dfb1400bb0cb58ea4adae69d667fe92b899b921a5dd31783f26145dee63242ad083e6fdcc90989d2fdf57b4c9ec187315d9e8cb8ed50584fca7fdae08fbfd2d34411db2b5c57b6991d03f887123ab0fd0a5c450c48e39357a053112c62f992f8176cf1c648f258750d0a47d97212b450cbab15c3e0060d71fec9b39a7bf96280ba3ebb516bbb4f3c8f180026ba77e7a1fa6a64481c98c5b6f93781c69be3a23ad733505ea5bb254d533dd64fa7a8634a18f0b51523739e32a37ca9e9919a106dde1b8992f2fabfc973b5f38d7d3b1813220d03c4f3dc1a094dd72bc5b2b5b06cabe3e4f4af547834694b5aee1c436ac8bdbb8a84625ad4a9cff3ce92b824416d14176d0fca6bc452e100b2415aeceec371d150c69c78d1af5bf854d28fe8d0e10789e44f8e549421d9e46bc48990aa34a13b16c579e6c8953262c8ec9848006371b9b26485ca867806e2305043527f33c33b2e96c25acdcc69fcb04005ff52d17668a477bcdfb06db62e47445cda78b5cb6094f99cc644ae2f97cb48587af2403fd78d171398769e6386a60ec0c1aeb0a80bcb74f33f334387a65d27e233e0a989c1bf2cb6b7f44ac3665aa5c0b0f13e6fc6653d22c64e2493b2a2c74bbb704e181e8b3a2053a43b7556861d49fc25383c3c7cca711c753082fe51d939e6d1fb4f93e8b2e2d170363468586d5c3077658532578c9a139e0c0212ed1828504181b7790dc3cf4b61e7f986479673f74fc9f5881065cbf09321ad9d4851c9f070b98da60896bd3e085d80a970ec25538045f990b396ae1344ecea124ceb934a110b05412c6cafe9c6c7aba2cf8bb0a5b6220ac3ea6c2fcb41396ce23ae5d12ec15fadb44fd24e645726cb35b80b4f814500fe91de9f610fa9c2fa48a75737ac721329c7803475ba0ac9b7a68162ba81c2100c1b1ddbcb8da202325304f861d7a0ed2dd1b91db742084da868504df566366ce735028525a0ee60036b35217dc2f08e23dccc9ab6f4868ff67f443a726b1f587f3ffcdad2956c5deec225b1d3dd3305a4677be71c9cf4a5edf2550397caeeefb6a7ac93e5b337df2ee7675bfcfc917c1c596a49a1ab8405afff2f51b986aa5a28b989dda03fe2a0140506c8a2e351951c408a0e5dbe19d8a0ce4722ec040547dc71cf953586c0f10fcc03b4f798c27184bd60837efef5fea31825f46e8cd3e5c885724ef62cb95aff70db60b7fe40f7ee3db53fe24b1c8e0758b724eef95d0a9cb915d6dc2bff7c8288b372fa8e516442a51a759f689d8ee3b50872c5b37129226d01d779f3dd5d915ab340d104a26f02a95935c07ab2c35fcb8660e460e42dea6867fcd34b8ebdc03d63d5750a21f121eb8398a7144cdab0e4b91dc7c98daf03f94227dc1b377932541c18a87c8152b0de3cc7d40cf251e332a5f3e0f4466bc5c556e8dbc645d0510566fbeaa3327a3da254b540f97f63be9bcbffbc55f3c2a9afc8800a89ec392697488e58d7c0a7d29df822188a9beb8800d77652fe24220c533e36422904896121cf80ac4f91947f3288b60df0b73270437627f79edc6c4a50a290ab04ec8a1cd4bb653c08507dc3ab933d108a7d35073b6e1fe66bbfbd2da7617e217fcbfd7756bebcb3c931a94932570b9f249d5cbb1d156eaef1dc3d8d406a36389fa543559c76601b4f31bfa3d064c38230b516295b9b132223a8878b42d7bae00e11b6c0d0de84762150ef357d10f3456b0624ead2f3d5466a33e94a19ce0f404ceede01daae1f9cd4704359a359819d3fd9f33fa1a27725840d32e212f8772e10db8e7fb867a0e73dd2087bf3e7673fc1438352bf3800d80741a3442c52246c7e383e3d1e4b475493c1a54caaf8109e954d3d68caebb90ebe3be233e7bd0ac83ab9dfd98a910b8995b2747c909a5152de94ba4499730f822670d8d8460b5b2bea686e152345934d630024a6b6a55a8c92fa5ba32b22ecccd61111fd1328df401dfb9b1bb40ebf7211a28dfcc09d7227a314333fe16e87c0d145c6b86e1131d1bcc2c9c282a10c97e69053812f28048a2e8592312619cad05387d764af40c7f606f9932d21cc5c8a4512986aa893611720a415a1089ec6c94ee994b5994f4333e64acdc6b726afa58d229641c15cfe2ad9aa6b07b0c3d714492f0614a36691c27dbed38c4e4786c975b10d51a3badaea2f5953f9d8fa4b89791c4810256fdcf8c0a5b0b1d04f5e1765b08684e7e1e5be7844742c279a3a6b721ec4abb18e503c13c002f26bbc4ed591c2a48fe38eb62f4ebe639655798ff0cd6b3593c45c254cbfb70fe3b4c57ec41322ccebbb8fe78c3cd6e3afcb83caafad2dcbccf1d479f8487d34498f0d7a29c9d2f4faa41939be6979721858c284db8748291f1f59e79cd2076fc2028a838fd4d136c2c53a95dba376d15bb809228ede99b62ff234846e8d79b1630525424939d0eb735ade9063bb789a10213b88a646377cc172d2362c124bc16316a9da602f112052bae671daa4814c3c557e4b30e85d6631ddd7899949657411440439eab17241f28238e18a8423428268bb2d8ac2f1bba7c7501504f7d74c838fbfbf6c4c32f7a77a45907a63f88f909a829e1af089a0a9861f364977984ff8648c17d280e0c5c529d0498a7d069bbec58923c0a1491306b5e2c4a2e89f27daa4649093dd41d7d037bb2ab62fdfbf67e86982631467ac4d0c0d803b0b5b4528947497915f82b11290cf8c218c0d82a12ed344569aa01de3860fb9ecbdd2f17f5c82a21a3bf021257ff2dd7acfb3f390b41708a64740348fc4ec134cfccddeac260e3057a430f1db0beb66364702924b80fea98e44501d8eb7e4403d76a08d1c572741bf5060794415f28e36586ebeef7f511eda761acd12ff8aeb5ca3b58b4c03b44e9ce372309b2b9f3b8663df86089972bb354c48d7d38a2273c466a91cd8dd2d71fccbf1ac7456664cd6279daa94c646cc9ba17dea5c65590eb5531e1fdf991f2e2f2f415469909610a37f62d9ee59a674f81d8ebe78336daa033101f936dd7a6d954aa0fc521283d08ab601ac6cbefe31b1dacb8bc79967f81103f843f889cc0fa0a3d2051398cb52abdf4bcddd3043e6eaea0f939cc345087bcf77b49a2c19f4ab6db2205c0f3465bc498d1b1f9f2ee07875504fdb9f7c1bf1231dcd49ec059eb71d22f5b76bb0f701a665afb6b703fa79852e3c6d27371d14dabc4eefd3072f113fb0390668382343279faba659117b644b34a51c88a73067a046ab7701648068e810bcdcc91081e9e8680a197c17e065dfb2e5d4e5d616fbebb6b1df02ae4ea251425c5822c46f8036681a227abdf7fa7ed84e6e15257fdcd0830dc5648fe30e2f417aa682b0e494c543afe329b4adae6a53b3fe04ed76f4de0d03c8a4d63cfcc94237749ff882b542700127b33317658b312073746c450d3ea58b5ed6c1d7a767704dd753702f48e19b72915d755ebe09547183476093c2529d5e2bef589c0d0b32dd044650720e56a65795f0a7403068b34ceecf040b774135bedd43b7b2ad11b0c434c471ca600adfda1e2180858f5729e7d44556a6113aa9d208dd212f908862a637ed4aa3fcb43b38dbf481191fe72d8784ad72903fa2157577bf6dfb5271b2471f38190595c7eb3b56b8e023ba32ff699e75896bcf3bd192d1716710396ed2112caa373718b77c3f77f34ea151597b053d467b35db83defd031475220c9ec2a28b6a362886f375eadcdef88b87b3c57b84aecb429d0573f5ffb0f3624bf661704b94ccddf42d78c48a29e69d2d13d2b4e06a67ac2f2bb208636de76a1afcf08939ece469839835025f0e579b3b394a56cd4cdb125f148764a702aac39e4a01a361644723ade9134108d783d0c26031867be39da49fc71d03fbeeb48941e6416fa54ede4a2d334c937ba433ff61ca4cdd3ad87f4414eb8bbd453e3e11afc5f0a35a4b11749feb9912453aeb8c75c73e1972f9073b26cd1486d39e26d76fccd1524a7d0b0563592edfb89653413932cf5721d415a98b4e08b79ea6eaf02af5a01d2a8c3ff6244baff5d2d02ce87f6ffb18a6580caf33eee5da6c901f2ac5e8d0ca29bd454a302707be876a378d621013ead7db4e43a9d667a313a8af2fc289703c66d20b6849b4fe6e2f5fdb75b5d8fd69b8cc883ea68f7b54f0790ea3e947d19fc5927c8cbbcbd634ca05bed4049ef4eb11bf42eecd3b516212e096bc5c3d904db39dd8f4b5a4eb81a44e53bab45976e769a43c7a20b462f2aebcf97532a461e49bf694ada5220e401aa192bfc0fc9bb882e44421d3908b9edb7a4a711728922d6f34ba9c86300fbef97a31b5086731356dd51608f8ac4ad2903d09b7f68b82cdd60efca7e5f46d48d09109faff7d37f192869e76725731e44380e659aa7660f3350569c9db441fe0e242f2cbd3c5676f76944ea842c2999d68492c322f8de03b6be2eaab796d530a10a4b25bc4219545214c632cb3c90dd7e36c02a47a8b418eedda0eb2f69d1baa29b3419bb616b29e8cd39ddca7c7f4d35bb9560f7296105f32a0e70c90a955198bd14543c67fcf7fca5115b01aae2551ac66fc040315e2a5fd13d413134f0f7d3c0bd18439a56712164cca1e6fee01fc8c2cdc86d13502b3fd3737d7297939ba754e877016d2dc5939058fc184b7f58b26af308aed8a925b8a94bb711a388ee4fca28348b00e0557c50cecb8fd4544e6d6b5af6677968170521b98bffe10c997a90dd6fd119e62434550dfce1505e3ff32addeae9562305345045e29180416fd0001463ac12a9dbcad68e044954fe604f80c2718010b6286b57d3dd3799af96ce143c0d2a04b02439b7290852f4f4e7073cc8571fece212695728e28841584a2f60f315dff91376d737fe8a65a854e9cfadb3b1a63aa4025c333b7cb9e24d01f8177fa69403b0aad581496c71f25db147b1787ebdb45a25c0fe2130eef2f3c55fd9eab3a89be571e3d63ff6f03a41dc4a47cb7486ba1224fbeecf0f855f6c2e954b536e40228543831ced74904581f9356e75ded5f90d02d50debd5218243359b6f38f9aa0c38a2152cec4839ea5ba7cc647e3f17ad4e114ae3ec7066e1c751636b31a4eec65f271286fe2f35ed724fde80b37d2181f42c7492240793e97702e934b62faeb1de66370e6110a989b8cffd44ad68cc6653263f9b9c1ed40bd3deaa1d80e8cb20512d3591e1611bf64a4c06abd7defaa4b52c1c810c85664e8ed7fb89feb7343c01cd638c0a5f63d119bba094ec411c00510aafd0e3a82da29ffbef328a18990ff941a97a9177a8bccfe5e60f17690a774f1a152d6d2524bd5edb7ed8e30d6d410ef11c62113d3bd1cd861f101b1d0026ab140202b13c0c21d5cae1537aa9ce88b574fb16e00715ff82ddeaded5afe48b6c285674603bf9ceb6f57ba50c181cd716244605828d74c7a949d227a698aa05fe15af20dacf7ca7811b7df2670cb8b7f91c32340afb13c6d1d6531e0475c2bbf81402cc09548d27596066c98d5cd270f87585e0a534947697ccf2350d5e773a3014159212f641b1722657797ca6189aa32918fe5ef8835f1d3dbd85f4e6bd673f1aa0132c34791f2d2cfb24cc7bc3f7f43a9b8211d66decf494ff79ae8e91c72d10a3ffb79b85a84e90e572ba5acbedb8754246c0531e5b12d492043779cc8d0e0713f4a81c960a8e60354a797401b9ad06d37294c43a5ab62a566d8dc6e859fe0b3e53294dfa5d2645c1db407f9996a33339bee0df57cf4ed5f822e2b3a63ae41fcd7e4e0e2a10ef9a1e4786a6aa63c6bfcf6872b83592105adb71786c056c991c79fe5189b53521f1b3d2850baceabb6cd9f12adfbfaa96183fa43e12ca72b32e943df706dbfdebd57135e75056f0be276d51e190da31143d741836e827aa22097ebb616cb6cf4adf5e035ab8d35e5442aab3330717c0ab5f77902d03cc98d42d2b49e29ec28b39ecb70f9ad99edd8f1f1787df8dcb66fde0357c2af545c56815081b036b8ddf90a266d46528db02e603ddb68c3f76fd034e64524c9bc672390b451d3b1dcc9c967f291f5ad242381aa90ecf15851606412829f6e22d6bfed703fd8cae5a40530831a19cc3991b20b6cdc4cb716244af876033e2741012a3d30e5acb082fde25051476252d6b4bbfeb1c6d9e9a3c8f3a1d277eae36e458c20409e74c7314b02800b3e5df775133e0ab515bffcd96353710ff873586a68f22ef1c84883a0bcfa37da9f3b60925016b56df268067ceb4b1ee2880ccccbe7f67ba017f79b69518a3d6938e48f4f1b91096f65cdc0f74765fd6ae811b2422e976c0039fbe249148f354b79079ef932de9f67d65334a3bc64fbc56f1d615a0e20d7b182f773b8bfa7b3769c2f32474b0560cc803d45ea7189cb72436048e96e691631f70eeb01e87c7d084152f7ec29cc7353ee482a17b1328d90d77688c08ca597a2eb6ad810846cae0b76021d37b510857a562d73698cdbdc4f54fec9050e230da2af550f98e0e00818256856d0983d75da2c79f0bcbeaeadca8483d0b0ad2315f0f3742e9e70f3abbdefed8d6cdd306a678952bc1bbb0dec66f73ceae932922ca0af9187ce31f3a8dba71ba83dd28e38263f4cf8203e52c0277049672b615ec5703852197e8f0f8fa6230f2b734edf0d6e6e72ddc23b9b59da7f7f62695408329279532a9f9aa192356967ef60667020bab4073998a31581b5aa2769e5dc4708329da847757780bde50232030fdf6b1807fb98193b7e3cacaf25a0d7c4799bf9b1d48e98daf16badf314d580c73d32693de4d749a2c748dcd90d7c5c43b854e7b1fe6fd7209d4b47d16eb531f7cbde3a17ebb240b12fb467d0c2d1e873f4481a40c930a7d752cb170079ece62a0859060f81062f0fe6be060cb922e3011addc060a5d3928b2fe65fde80aece1064a6a3cc5da0c9f3a7e98e2d77640894b2a18c2234fe7beeb55b241a9cea1abd795d4d3b1173d4789db2a0eba90a09078b7b29a2327ca87ba607e5e2ce9823319fa10845bbea94dd7c8601d050a2d9c230342c007091c45a7604d4126bd5ba268bc6b96bacf6eebca8e357f96dc9196cfbf1262208bba9cc8c4468f9e020ab07a534bbfb4e19e9bf2d1dd2fa7fa46a807c5d2191998b29eb8c1a2cc49c5d89321fcb84a58d017fbe187f8258450d6743390da2f2e330329d0cd738eb9accb2757aa2ced21ae3be0641306c8d05e7b945717f3a22467da32307db061fdb0f1b7ddf119f9d4c70dee698b9851a5f7afe39ad0517b0f30fb81e286136bd256626fefe97ef6865c712bc0ea64b3c05d5c2671bc09c22329005bbe9b8cdbc6ebab0727d1b5f2be95500317aab579466744c67137c18107dcb8773498048d26c2ad069820b1be6997bd7d0fd2a9efc58321278a3815ee1290bc714c460163c534f2a67a207b72771b9f75b5e09772cedcc841485c3f761c34f787a0fd0cfe3a349d19616f1c8f2f3408b79eab7402edb8642e4fab97e81b84f46a8a7afa6dda8309ff50c780133c4bfe41391118ac47da47f6c64c65310f466d93d50624cc5ae498e137766c29992e4f540831934b104f34317917ec361d725b9c2122fd72318be3b95e1c228bbc5ced08aa9178d4c09f1c3fdce06b3d0ad6aa3ed19456e163d727406b9e1e12359a8e1ca458affba8795cb626ee243e056c563d07018c4d5576310ab32e0ad890618d5bd0b4c340df49a3324601e0c61669fa62830383f3ead58b2360986baa7084f38bf54e4ec380d9ca67fd9563b61c42ef088fad28f92bcbfae2603a3bbe0e3b3ebea40bdf7f0d1b00a997ff5e1b15e487a2745b2ad061b89772c2caca6942f8ea111d61349cf64adabd842845c7037ac83db56f677018835f0adec97a2e23d615a9affb8c114ae30474af028d862d169ab4add11ac133a9f0f86ed72ebe6784249b62cec4dd70d66d842a7004271fc4a532858f1392bc3f447beea906c3ad8a2600c296e80c72501f1ce9d0a3652f1b51b6707c40d8e4b03a907d3b61b8d77a6b28b0361d25f5fa852bedd4308f0d1bd4617a908f95dd7d4d61f02b6299c2efa5daedf8b276ffc60fe6ed7c74f92aa265ddbc2997e483ab7d72020b7bd63088c46ca4387deacc6af4becd1d514928a4e52bbd565a9ff3ea24c8cc32653327868e836d1c6d70d9f2dfb85f8ad71b2937c47ef2e32b728d43d5fde76a027353ff5715db2825010ee75c02f650b0d31dd46ebca018f446acbdf62a40fd7f462ce908ea79b56992be5ee319cfe0c5120dceebbf0577e73fb6f9522ac9291b1aa478ace1f77bd3c27ba8dbae11ac01ef1e311c049104ff89d1d03a814aea07d10571bb6c3a1a03d199c166c9d89c8a6f0d8817b59b5f96711d9a76253cdcd4780752bdfad35ab53f139213516a0f590206ab60816a58c3d37c7289c38e5451785161a99fdf0a8ed655253225d2a472a4194c4efb32d4241593cd470a69d14bce7e77d62b3516377b7714d7cc72c528dc2147d770e033a46e87c8dcc59d1f80ac013a907d88abcb8ead6dcb38b0a316d11b55f328f6419cdbce83f354d7c6c21ced11005f2e797af3ab2b493623098b0408c68bd7ee4105b99ac2582ac6934481c35276e0e97f4282d52de0a9c551d6a11cbd2c6ac7b94d2703b8970beb49631fdf0b185ac097894c055a4f1e25b6bd0c09d60a6817d44e48213af231b0adc86ac26551a5ee1cb95a63ff0f6266d4ac6d08247cafa25b0bf66ac26718882c81826140e45d0ed00a923d0392d7b94d5bfc1d3363dc3370c556c4deb3aaf1f6062dfdce1a64ffea0c70a7f3c4b596fa5d8c0d199eadb28c99f5d52c29d62d8da00274a0a7f995c4986424d1ca434c105a881c53e3433fd5850470241659b7cc079cc4c24ad05a0ca557bee7144948bdb43b178afe29a44f2990bfa8c1bc29781d110d55e6819c13cf141a870b45875abb402dff7d7b76a82e59083e99f95151f279e23ebb951eed4566d885e7266529eefd295b7597c2687529f861e6ccf0cd557dfb67ead6417b7932e5986058bffcc30d082b82b37a193b1a4707029f3537de2804a812d6163e56c3aaf22ea3a785c86b1dab7793e05c29ea6b322854a1134295ac17658626b964365cd15b4fd9ff16c027a54afc15889e3007a0348f9e7bbfe505d79a9216d794a873b4c83d5a830e5414860baa803026759fa3bcc5e37172863e6b7e4d70e344871443d0c5cab8d4b601a98111433657f2770c31c112fd57cc42c9245cb151cb1aeadd23a026e436d723a267d29760a4b339780a32c41c7f5a6510650cf324e6d688b758f2f9cebf7c19347c3881ed00a950e0a3cea706b404f45e9b1d8f608229d18ec6f55b4d2821179d341f4e781ac9094a28b5cf4c1e0c6222f49a788db9c774621ff49daa3c4200b0260ec89dc1889f9fec750ed80d6c3c2be8130ebae08ea208cb1dbcdaf0760b4412c2b2183a26cbdc043c9ac34f6b8d15c9cecb94490b951a8559b914da3e0f5a32d3350d6e7b81e3f45c78f1837ea1fcfe64e14768706b01fa1b6c4f61bb10695d7a599f27c0989a820218fe22c5676a8bd30db5390abcab432ef521b6341e7d89351ec3c66305f2c0ac6b1cf4b0ce11dc6a847e6583a96a934d20e464a6290be30d8456c1aebac52cc6a5d0d7d260f9df4bb30bd088ae6362b3eb6cd9b6049e506f33dd3ce4078afa704c361953bdf6c573203529f2a6a0070f560809722476d300779e00e14bbac5a882768ec7c26acc505ce1daa807301b883b04dba7283c5c9064142e834e6713920dd9d2d161d0b363068a731e11f14c5f792b597a0fe87b75e67c58d24d76cf08dd939e1272b756e5eae7b3c620e663f10f2935fc1ce194e5b7ef43a203a8bdaab9ca888ce83ca654d5e59270b9d210d7f64a198b0e12c66d667f7f71c4cb1314ec50ecc7d129debaa2327b182257e5648a7a27f8205433d8457aa829ff0eced68d08accbadf6f975fd31159fc4c097ae81ecb1f60f7fe9c234d7074e92b6333b0b819cecfaffd18dd2977434319c373a0e4d34ff1115c1b80a81e1a261e9369ccec0c082defa52fe8174a668d8aef64c2740e9b107f6a805616f79f4fec5900fc61792c4a71f2212f6d59f7475329eff567da947133d4f8c0c07b34ebd0db3f01aff1702e6b2c1eb8354f6c9c6dff3a4f75259da0b81d5a2a0b1047d2365ccfeee0ed9c96cf199cf703ff80c9afa6d6224d30227199193c646df631334511cb4003531d4f0eeaf19cfaa2adb9915db7011deabde265b6713610f82d9ced0fa5684bed0807b31c1b27707bb1c3c6acf4a5407ffac064015253debdefd6e0054d89a405308fbec4f9173807df2659340190744f4b5232950c2cd17188e521b81db6df3b6b1e39caf938913c13f93df8ade49a5ff4a07286c295dc83dd453eada3bfa1750fa12bef03e44f24e171a622b7212107ef4091ad7b9e66eec303955e11c51085ee170186876d7c2acbbec43835c6c97b16505f41043d8415ce73b3bf2de37811737de54a20eb9f050bdfcaf0d624f5c3dba2aae5d982d5e01e2699dbfab33228921a2876caa668275693665268fe14f1d9f127503ad77cca35b54cc3f1475ffbfb29900c23cc3b2c1512258f9bcc823263e1bd27cdc52dfd62dad4925033970d39f0d65114b0118963cf015a8d58973e76d0024ba4de3892b22978e5e439cd528bbac8ba666034052af00f9402844683839100b8c0783efaad35198945c3d98726886c596d91918427e81a222fe4426184a2070889b115fb5bf0c4a757cf56f755db917ae10525f6604cd85613dfe647dcb0507c888282ceac7b445f109811bf5f3d509d5d5455c9a9e9df1693a87ca3976074bfc8a21a1ee53baa63cca744869b825c4f57656d8a4102448ffa04b994faf0d8340fb40a86f67d1ae0898ab2ab863a763cc8ab45a58c96e02f5d2013107632608637dc8dc6a019d6af75a1ccd7b0e350d0c8e5a161ddc1c08e7c9363c41b7126a7ca47fb8a4bd3e38f0b529785cdd02109450bdd55bbcb3ed03c755337b0e1f0bf6717ca7fce898a35280edf6f2c6b47bde31b0a86ce835a0dc9c10f891ba0c7fa8d1b2c54e27d37c5adfb06ffd4c78ff6373fd724c7ce69119365cbfbb0a241de27fd2ee94b23135b9df33621272e75d9cf9560635bb1eab4c6f60f75a84009783951dca4cc3f935b6827c4ecff13f473b7c11b0f96a5be7718b24b2f0f62a5f7ed6c5161ba762869f0c8b6cc009b07cb8452ae6104fdb799767434f9ec52bf3b87f7aa35a13cfe46e2318be378b3f9bddf4ea9223442cfe05c4b18bac0e271f21fc429b31ebce90f705c31f6c19d9781feff3ecffdfc261518d454f12691da279357dfab133eb08889699f75174df8dfbbc381b48de70c80a440cb666dc0a7030f8c8ecab1c1c97d5c7f2645d60891a391208593f8d7e5aa04bdcc9d50209356eaa0695cb0e5a49b0dfd5440d958309583540c02d13b3fcb4597176c3d50c9c16dece31449cd2574a45d99fc4687374c94602102f5b46246ff60e6b37372a6809108be44bf7b89c493bf154df1373f8d9086392977ca13e2cb89de5adc219f76c71cf919bc6bda4d0fd6d0d07d4ec925b2ce2ec19e9c3c0241608419d861c096b79285ba465534064948e058a3bee97438896dbb2f4cb40c7cb0ccf41af0996f8809b176892e0c5da063c36a6930a3b17308a2c4e8dd0aaa51e72b29bf0215ca78f71143567fbf9374d7e9793e93d6a3458998b8bff08d5709f1e58f63b0736690dbb85283c9d46ed82c2d90a3bb69c84617c7c88e4c5cc9d79d66dc710262151641d45ba24f9a918510327fc2d31b426b92795ed9f090ef559cf30f1201f8d0d0f78d7898a04afc5fe83eeccc1de82945db199064057cc868e6900ce7c05617799dbef66552ad082b83f9eba55a7ae4fda9acc94e5c8ac7cf11ae65053062921b2287074e630a6f20c1fb6acb692eff24c1c9fe9a1f842b571fff806e625f4a286a5725188640abced4f491d781c8597161fd5a4bb98d90ef9826f599325be04d44fc983ffe469a70f28b4bdcd7a044abd612a4bb4ce22cd3590767d2d0f5a272080032fc298aad59b56a8c04b935c4f5f61f2c882ff010843359cc5722702cf8db27c7ebe19821f6a1fa2b45378ccd62678f2bbdba7fca7e666e3994a17792e281bab19d98bb9e4ff096b79c3af935b151cf0a796a74874ce1dac0bc11dcffd953af6e5d33e9a44f4b6b5cf11c1adb4658ce8cbf61a3469ab8a69de5e08c0469b21420d91cff9bbf8d4b0d3819332ef0e3271d5d58b93a79598e119ba1f61b8b893ef57a83c6bbd4bbf2dd11dd7688b7eaefac01707fa0fb1c14976711a468f53c7b7599ac1b6c1b36e96d2f32680e250b2f9d1f7bf7193d4442423be150dbb9b7e88f9773311590cc888b7c93146e9c869aff64029aa405e9a02de9416b1de1fd80ac6fdac97671b88ed413842bdbbe4df0a6d58597d9f1bb0372bfe1ead477ea0920fd1ea8163d17bf85a16a33328d534c93da80b340b1434000e58031dc61005d0fd180e6a14487f716cb16fe16b88a5a18fe297be374f46b99f2a08f971573d23bd50fd55207f764d082c45977064c78ca0c98f5b03aa716306bc0c1b172fcb4d88b6c2f082316863d54798c916edf1e8f676a2d3d13d2400989f093989b19720188136a6402888ca5b6aae2ebe548f2bd13b433efeccc791810119897052bbc6759c9b9c133bafda465b75a2007d875aa59572c04dfad21609c5131bd9f0ed42884c123fac7a35e477de7fcdaf2c6c15e80c7f2de1b7ad5b06fb117a1a56edab8637fd959382fe27bd2cb7b7e0ba2bf58183aac386238fd609472642e995b1dedc27f03ddd11ed8513d43058ebccbad0530dc9fb591ea271bf0b1b19ca21b1722962c2ccb3f36299db873c652e6fce58e5bc7b0ad2e88f7adec42cba039aedfc64da34a85cd47c3af5d27c4edea22a973d5d6c41ee1ce66016ea215a5276ad1b3f1d6e3a016692962a9bf07b77ebfea88e47516d890c8d21d5b21ae20bf3e1422de4c93d5b061bd8478db46472da3e5e09ed7a9fb454eee320b4235392465ee8d32e4e2d719bac58417d831b9c3dce74aec833b48574770779380a3fc55e22da21db976bdf202006299dc9dfe22aa5de25723b1ea5adfb04b8fd2bd57faf1e947f33379978913e7ec27f4e025cf013a7f20a14d910b4f4030a0b0670a5e458d75f5de7b8470964d20e02a18a292d3583a6cb04ca0b88342b61d6030e48bbc07de132df81dc65ad5b3ed95c2adfab886dc91d151a9d4edd1a1d87299830d9b3137644b4cbfbdfcf79ca04845f9f205ef074d52598a0334d44a16d0596c56820ceb0c74c6ed64b4e038380ae235f3a1e03092f703b593e90a111ff06812849a90f4292587df08935efdb3f016c7eed02d8fca01111ebf8cbad23532aff6c0823528961c992e7a60be74c514cbfeb62c3dc915df54e6ea945195ecb1a38ce6a1d6d855aa8e937ad88cc93c2350903fa0ffcb7741d5d69ae61ec19df12b28a847765063a379bf3063f036b000bf4857efac2e94b7e3d4f497d2c30192f31eeef0059b5f1a741b799eb2cad48805df63a89b47c4d7bc3c197848fcf952ce299c4455782b74e8548982cdc5e5419342df6a271bf286c4040ba56b22e7b0999cfe1bc2ab84d82a5a7561de669b30e15153a7526d19fd36705776faeb40f0aabc1ca6017427fa83fc862dc0b6bdcfa4e75acad8a5bfb070e3a38893461fe0bc647b4d478307d5eb5949e57b9895ff33d7ba47210ba78711a0a77669d04cf2a8d0c0af5eb4a1462aff2fb866e200cd1d530bd23c40ca45a8833242363eac30836a34e5a7d62438533cc839950f9f57d822526fa23da72ef4784349ae6d0ca0ef6b88b75895ca69b8163e77c95afd817bfb9f8f215f2d87954f546a007decd0dc668c647572a30343d9d12fa69845d50cd2fc6234942b22b9b6d8b6bd814f72ea1dcaea80d09e992d120228e1f67a8a4b70c6674aacd146f0d881ab8130abe104df4ed61f189f4889b10dc96d83e9bc6e872b54f626379d5e14be79625913b1e579c015c34a47d00a1043436c23bf6b4cdb23582f920d2b9b5546d10cc0a8c2d1590f7e5203d7ad40ca70782c9b35aecfeed035626e5de084fb09f64b20592", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c911096761830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000020efbc1b4676f6018902362ead77e27d0000000000000000000000000000000060ec24b679943ff559895776f6f11c442197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e2aadc7250d71617ec5c2f852c2e49ed3180111ef6b130ece8b3504a7e2c3d0372c30f9e94afea47a8631132310e6d72615ccd3227fba92d2ae829172e7bf1c2d24fa1f864f85046e7f1b15accc6c376dfd7c6960cfa3b7fa0662cd10007c0ef7089943510caae41bc6adc5e284e6194b797027236225a98865400f364685fd6609bd127e0b1c620665f422cca7d6dbde8d84465e9268825c7e12feeaf35902160bf36fd6ac0eb33261fdbfd25f0c3cf65632e1be186bed5531a223cf9729cf8d2ea020aec653a1abf28081da5f3283ab32f9ebac85d38f7fb2c6b251fe701d34" + "proof_hex": "0x00000000000000000000000000000000000000000000000ddc537f44b53fe45e000000000000000000000000000000000000000000000005d2c364993f27cb360000000000000000000000000000000000000000000000036a97352d8068108600000000000000000000000000000000000000000000000000001db8386de0ea00000000000000000000000000000000000000000000000002bc93d2aa2c401600000000000000000000000000000000000000000000000c0d14f10c5fbb573a00000000000000000000000000000000000000000000000c707bc7be0aaa260c0000000000000000000000000000000000000000000000000002b67cbdce2a1e00000000000000000000000000000000000000000000000cd16482194b961ccf00000000000000000000000000000000000000000000000b86ad9a1e323001d700000000000000000000000000000000000000000000000de1eb22a6bb20778c00000000000000000000000000000000000000000000000000027eea9bef8094000000000000000000000000000000000000000000000002fc183d9b404e5b4f00000000000000000000000000000000000000000000000f8385a2178429603000000000000000000000000000000000000000000000000cb22ed841d6eb43670000000000000000000000000000000000000000000000000000cb1fcdefcffb1d74bb7961ace3f2f70c4e830de967288c03b173bd5158d3e5cd1d7fc55e865a03cd83a7ec11ca7d54aed446132af02f106c160b605361787022be5c8a7d0fd01c35e411edcdb143c708c052dfdedd4e54fc380814b419aa5341068557f1e98511bc88a7061ee755e52288824c391786d6adf9a09090ebdc23941b5572d247e416da2f3733c9efb99ba96eb277e14f3858272621b15735c4cf986eb6f9922ab32bb3beb4a44733ebabe5d94219e318b866bc934894e61df208758067275f6a7e1683d765e17405f0e76a58240bd4c830d911f26e464c227e57d2e92c9a1e85210ce7ac20d92d0db6dc8f9b9f63e554bb78536b2301522f2901d1fca0b0adee9402b670ab72f4bd26fa49e85c45951a984d4bee6c4bf2070544238ed41f1adaeb1ec052c80e75de7cb223007b5f194ed2f28d04cede81a64ab07783f001b76d942d16c4d740f877adb2dea5cb85ea18004f92f4f00e54481fefcd1115186ab43a0acfa1ca0a228aeaacf7ce2429f7a4e9581a243c4029a443ef72a3a321e1682d14a7f2f2757df78eff6768d1f653b2a2994b47864fc4f80e532b46354245b7a02a6e00ba0057129b07cf30279612ab0a64e41eaee79db02dd32ee1eae16f626201d6f39a4950369f70b76175c6dce4ba02eb128ebd52cb75682acc63f21b52792eb92c25245ec066ae29f6a9b69e7980e89a66b0e8ed0b8461e1a3cff3a41629169bb7a7faf7b158af57ac2a8814736fa48f50908a1cffc783e3dcea358a8dab05d4e8b1394a69227bf3fe6686bbd41b65ea8865f9616b853e55d01c263cf4601d3b429b3fbdbf6c3ccbc8bc7613a55080d4952205bea66a6487d50bdfdea2da1966d442fbb7b6d5de94a7f1f66b8f2be77d33671744de4db5ae847949c06e0509fb78c99816ad278049888bb597f7b08870142bb7cac68ad7bd9866571943eb26f4a3efeb2de8d56203502ad26bf6d582fafe6e893716505fca3da244cc36a329057929704d19799de43956ba1863b26da9a90cacdd883e5afe90272482e3831652a8b3992509c56f1c37be3f7da0d5ce3f5dff03f2f28f9f8971e78c0e257129790bad219e62911680c2b8262c491a19c65a69e2801de14920a8e49539f7782820f32def40017e1af00ec6d325e4c82b26d4dc738596294c15ecec975ad3b00ba9388f8956cfbafba1f6c76a2a9367c27738125e6f97cad3118711c432342e01a4efa9335931898b9d34cab011ec52bc1f426517facd578e65d3937685f8192b4abb55aaa96dfa5fc246f5bbe5bc9777f78e01e8c44a8fdb90646fd71d0d641738a84b0f2d2d822d017cbd5c5d9d7363eeb71b6098d280119b1ecf9b90f4610307dc478a39d30eb8998fad0001bd00ff2bed07374e5dd387830c146323519e08f35ab390d4282810ac470f17b1ca6326ea2421d6119c6742dcaacd76c30286282dba21e7d7389bde163c696622d88838635b4a546d17f1600c18b748c9f72c2553375c44b53f71a52bca0c87762df6552408a5f4500965614a5b03d4fede551658351460482c296ccb4154aab50e4ccca5cdc55fa75423c3d84c64f70518b208aa01ded1676e0474d468ad377ca4d2b89ab3cb3d3963de0a6b13b1debce552033885824e78ea0027af972aa760ccfc5e7fe7755a65a6c6e4d532a33aa47c39099e1828b109c1ac9114251607526ecbbaafb83e8fe2efb0c5388ce62ea3711b2b28c132f05e1a371ceaa48cbb778b4db0530d863efe66dc5a00840ee3bdc0f71cda948b42a5a7059b54dfbec5be13a982ecd3d08e07db4e9b740afe7b136f6129ebc34c7ec5021908b52d65363cd12854f10066c1a0e09a9733d4a304ed41801ee2ccf5b72db94d42ead97f746f5b92eabd9115d8d3c47c7198f0c59f61c82c2e6cd40132c27b3a7014a3d0404857ace260b2cef00871951e08d2fe4af80cdc20b67b6dec13d790f840cd45bc09fb36f7e9edb1576aac1fed6309d69d9b4bdc23a372afc89b2b8c7f8f0b4d5a8a9964d817ec367098dc3baf84a14233a8b24c160d1e3cc9588f8c29ee86bf38c8467751c54dc206ecd20dbc041b9edb0efcf721eb824ff79a781cfc29baa77eadc827e1faa60d2ca595aaaf41b05f47403df5054d2e48bd550fc31e258c9aea79d587deff0e66ab7e24df5ef60a675dc0d5d61b9ff4f004a2ccd4320ba46ab42593fa9ff56741d268418ad57e8cc69060e112173229ebb2deb9626294c2c341417b9274d7624bf0f7bfad9d66b10d28b8a03116f1913cce922e6dba33eb29a98210fb69f529a830d6040c5f0117608ca3ceeb2301f5fa825d225c3bbc88b05c33ea0e41eb014c66441d5a9947fc4680d35e421dee79f2143d0497c9179af370b754e0224e003d05fecab656f64a823fefe44313b30bf0007ad379d1ca866ab25f493e8e18900fb389ac5415f67156120bd77c1d83e3909193ba0d2b16ad19811ceb4ad3d742b50246378e6fb6e45468e9008a1775f8bde9dc6a8231c8f474efdd6f11f695e86315777cc234f2c04bf87252c12fc8850ee7143a98fb139e12b5cddcb9215993cd57f93020e31082160c1540fa0ee8fa70c9c36ae886a7e4a52840ac356a9c28b4a0cc432f0a57d049d9c60ea52f9afcaa6d4f8d79c21ae5b04777811f9960f0fffd52580320d0fa196009634e0b20b84cf5dda38ead8c9dfd830c5aeba4fc6a0d4ef749993b144954888993560f5144b9c881f632066f6c63c95f4066fafb419bd9601c028ca2546fd6c3efda0b99e191422cdca5d0e71bbad1c8f5e358b973d75042b1dbe0d437393ada02fe22b41dfbcfc9155f55bcc64ecf82d0a67d810009043901d592b0a810c89a910b1ea0e956bf5eed9782a9ee48cc3a97b989f1ccfd38e3a2c24cbed0109ef89d151c96ad5afa7c6d2e7219744a1338debcb9f41485d602f9c2cf30efb8f22adf390291fcf0d2a9ec8464d8180854056c10dadbb17a051f9c2fa1389962ae877246190150bc8fdf39bb570e596c3ef14377483c735c8325a32b2709ca1e939c9d3304855cb7c5d17fad910f9842c598b174fa9c275096076e45af017ffe4d0f9b41129d3cfe4ec19dc4f72a61ce91e290353e9d411dab365e3cfc43583fb9c8be7c2e0f7417e6953d9c8116c57bbde9964dc413f454ba813aa314c4c9dce76b00c9136add93dccb1318d338c95faf456f6ce48ddfea69c406741a3e59dbc207cc9a190806c021ece63a687d90e4ff7fc6172c53f1b23827409310b5a44f62fa60292055a94c5a8d3471dcb94e967000cb2decbcfa85aa6bfbe4743e08a1ee230ace1efac82370ba2f09fa77016274bbdd487475e100862dc8fbb80382bc69de285c0bd0047ba0aa8ec022bc23168d5afa5b1d3a1ad7bd4b9c2ebd431d3bbce150a70f29dd3aada8fba9f7cd5d5db945f024a9dea0dc51a27752f0be20b4483ed1f5134d34dfc8dda93da80b5cb0135d3e4f1b3acd5895afe9f691dfe5a41109caf52391b88832ea4c3d2b8627b03beda0692826fb896c237e415c9058a78be861841bb561ac86c2c8800167ddcec4067160125bbb52a30f0a1c7a8cbc2cf4e1dcc107d74dcf175389be3287894d9a8ca93a7f13f4b482f4d96b5a755388c175b67f077bf625750b2cc8492e3829624f6acaf7206c352edcb0860416f30c5900359518496a52f58dcffa59213f02a3968e939b821acf25c1d54ac8e27b5037799a971a45440c2bf0c8294f291765bb3f195dafc6cd825641f439c909de60f22df7d90570f8581fd144d8e336e732cf807c575696fc5974beb6d327d0f4e50fdece5c20104dda0e51dcec48d30f4ffe4765a5fa9f4a2bdcc2ba09ad027c2ebe4188cf02c493f0beec1b3aec852dff73de5ba65fd4704359f4e7954158bf624d937a0b09c929bc6fd95c537456c59599363f86fdfdf5b7d2b58ef05fb1b62c4f93945e1e9b7f217eab940cbd9d3dc027a2614a0e0ac55d6a5f4fc517c77a610772a6d5067e09832583efad6905bdbec52a599aa83ba05aa495f7cff70fe7fef1e8f9e322b71ffe815a45f39d91a5606a09f8725c76214aab8c32bc3a2ca3d1c4f6f342210f1e70ba357d8e93e48c3fb547aba790c9548057837506e9d4023e026050ca154cc3aaf0c7d60316c7f56a6ba004273b5c1a08881c83cd592b770e063f5db6194b4543e50e6b2d4fd1828703ebcd61f0e0e8238c7c2907f640ad2245e6f8df296178c5b6c882031dd6f0b4769251c1cc7243b4137447d868802cef801b2b7c214299d10918e2da7ef9a09e2bd9e11432798d3054d50e494c0da81d33d89a2607f681bd3f241f731f09477538456c7b3a0157ce568d7d4c828a4c24dce4e2b10cea115a9802c9cfbc41c26092502ace91f9937f175d77e3f165ee57b2ae6148293043f2a0c21b1110c61e1d48ebd32277db4f3f8fa1a44148cdcf7ae17d901310ba46c7249cb240b5c8e89ccbc193ea405bd845baf1616b538abd45acd32e250b0eb91ba32c5f67e81b79cc12e4b202a3a514ddf1c33f55d76a78dd2a6f431216cd9459be0440312a230538e2c08e9eb699323c3ea330fca18c022801cc6bcf1be0daec57bbbd18d2b11084198ea0cbe1d5b1f16f4c9c2f912d01e6f8b386610a28f67f554cd1490c99771a65f50623d2fd504f9c4ed37c92011ff0b00fd73f2e96733327ef59d02451b02b31243d6de810ab7d4bbe38cc5c4132712f9e649b292aeed91c210ad7f26e8b460f9a2cbd737062f3479f6790b0d85e6b128901da2c988129a0ed53916b48753bbce885de1a2f74afef8cf38b2dd4d8aad9fd5a7e13f32b602a56d2fe14c56a40cdb217be8516b43d718ae6218a02c8489fbe23562cfec06264e235fb768619a1c68daee1f5343fa2fbc9dd496b59aca837eafeca1c526e5ad567e4a3c658bd6de68af23a0a436df9f7f3f29f0ff683cc517a04580fa8bd2d49661956354885fb019f0cd95b7b7db7d8de723a7cf201babafb415f06da7a7d803e8584a58a41c24ae54f7ea86798affd2ec39e5971f63887d3bc631b8ebd9aebb270eebe21f676d194077a0492a039335bbc974e1a7e8e5e3b187b005cfce57954aa83f4f1373b857dd0e9108f15b8a3319cef2baf99a947447a6e0970c4556b9cf6a5f7c9b1c4a16e1327cfbb978db10ea891a2ef12d50a37cb040ff5378b30441161af327ce465ca56b4093e3eda29ec439c89dc32425bd33a6b0259d8d1d4319bab6eda9a614f0f78eee496b69ab9fdd9aedc3391378993c459027d87ba74779c68221b5b1e24712803e9f034793753d49d661fcc34af88b67a2eb483c76fa9350617c43cb676482acc4e4516cabb406264d4e41fed5f4d800b197e44dd76820dc1d207e1eadb5fbf61ac15b6dd1c7f34dba0210c84606971fb2094e779db97d0fe0250b61846c330025c5c951e7ea310f0482708886d6288c12527abb45cb435c1579afd6dea2799b69e5298f3e6325f2926583697b5e2b5e21515e902fd878d2706b6377e4a86dd3e75e9f1c6d5358b69898d7579e05df9ea09955053c4b5d8f47e09260b20094fc317222707a15fcad33d54ebb0a286f20522163de47ce56a75b960afcf3ad78fe489bce9499c2aaba40725da1d0d3753eb23e9215f54dc85bdd4633ee04cc3feac16e1961804fbc82b69144f0d02cdc09b08635aec4f87f85d0f8c2c235c257389f68de57c17570ea93bbdeb89aa489c7e2d06f6ddde1845c36d494c75adaeaf4e7f71ab4c3ddbff29c3a02b1d2df0bdff2fd17b65e8f3ef08cc0bfc3298b30a9ecbfa8748e5dfa71a6c479b7c5a2c68e60b8b5d197e76d358697acd62ca4d0f0b4ab5c7a1a6947508e0f1c5f9af1a4d2402ac2c8f342cff48a42c6cba919a995820f017a9215ad63a7cf6b85aea4bb44a2ddd40c367f7b0d4db0755f1f0513e8524e2fbcc4ea1cc65eb312ac3a0ff85f4287e803ad96215b8d15abb6a304a8c0f13bfb0d673d9b9a188e8bd6b56de3ee426c3ccbd67dd0b3996a9719948469dad9f074a13094f68e012cb4f6dcc18a9d92c504aaa12f96b40e0dcc1822332351be7c39d99a32b099abd4bef860b50e58a03bdb1f0fdd97afd4b44426fbbd310c7162282cb6cc5681a5d34c8792e7671840661eb520c57338c07b2b400bff64f9098a66d2aa940eeb9dd8d080e274620b022e6a97b9dea474a50239371cc56bed9a42501ee62a633aa6808daf395da3c8816947be7efd0e3c76672fd721740f8984d4c0529770f90560ba8a139c4f7ac6523acf595db0a22ad14e9552813447cb062fc03588da0bec34a24d282a7de1889063cd32f9dba2749bed618a058e3be4c300735d339e3f39afb597c470c287c3620c845653d8bda6ff9403c4e297a697a09dec0e9eac2cc5f590f152ddf12b0bd1f0425e8814a418457d8ced41d0d54e10057d8fe626ed42f6a69a7981809fb310a321c501eaf6f32dd062bdc21a63ad2b9a509a0d7d7362972449fba64911a3f0c850b147619d6f54f6437612fa2c18d1bec1016c640fe49367c05a1096894e4033f004ac462a4c4fc2aacf97f4303834e99878ccd540822891a8cf1baccc0042989e91fa78091f21264783ee35463fda706bd6bf6c3741cc7191892050c36c611395fae2e9bcdbb8d8d4a820a1391d5b4395cffb38f1a23d3ad3389c4e13e601842eec0dc4f455637e9912489865c89a373fa4d6e0cdd95b9a36043032523cf197a2f7a626193b18e466a3f5333ab0a576b7a6c572c8b67352f13d570809d6d0ae6457f3be1367321458a0e4cc1453d02460bc831f9c268488ad05f1433ccd0271ecde0885fc671c03a6d62646ea177e0bd6d7a3c579ad57ae0cafa74ccca141c15b844d1c1ab9deffc01df3f7fa8396ac7fa4c7576f689f22949b2915bd6c9161b73eeade29e2603da9d7d86e6cb95baa6c8bd0381b284378b2cf964fcbeee2d05ae518781466b3302f7561b144d6ac87de4bc42056911ca7f594555e4a5bd2c228205de015357c2903f7d9172e9789359250088e4797c458c6f439f57ec881fbbca82dc4ec32547233fd8b9d0f464e8c9cd656e151e6e7f573cce989e8335259537582f5e8096dc5b6c1a2d5f1941b9d01761ab23811b7e86df9d9a9ff7de2304576421d982b42b61ddc6148e03a73deee2e734616c495af2eb08c76cce640795dff87432b303fd8552a8e0bfc097667dde8f1434a2a4458e7e3e63b2d2e31fcc077096bd6aa45ffe9c70ae160cb4d5e2d28854bd889ba3155ce0ef9d087002539dc1ca5eaa4c94ee4f6b8f8199a1ddc619a061c261bda03b79c8c1ff158124ddd94fbe30c2ba65f28ecd47d77724340ec1fb1143c150a014e2241b78c8801364f1b969518cd9d8a46e0a4b2526680351086bfe08d3a0611f5402fc8ff7a2253c9dc9121f43c741d9059fdd5a5bfc68b8367576fcefeb2103d8f2057614a42e38726adf28f52dbc15715e283dd4d3e315ebea3d87cb5681e648783c45631b17489def600de32fe48770d6f8b47af009996c343952dc446322afe142c7a0ae2fa2ae3dd68945a1e5b1cb742a36aad5bf6ece555e28863ffcbbddd985b201132a1a3a08ac58724b1cd5330dd46b18833b3349816a5dd935d98cf89215f41bd51294aaff14004a783e89eec6acd648afb645793dfcd5e4ab7a6406a7b06c85cf00533eaf77c6ddbff3d25aa4839cc5d6aa0f14a5925c0a32d9a445768524164e2da427ccf8e54b7a9bf52299e01b91c868cf5b4044b45ef5ca65da8d3ee799fa20549181f77be66bc9817005e53bd1e531e6dee9dd0f20ca8c3fca42ae8908872fb3427f975bf8bdbbc304d78748317f4b67bd3dc7de4fe9b1b157edff159e561ebb0f00f5b33cb3909a8a497869f51887c3db6a9b772f81fe2884ede9caa523046986bd5d116527e1ea70b1f80828a499e31debc9b76844336025ad1b21b3d2015c6b1a54f0545b2cb529e8afeda16aa48ce0d0ece560f74ef2f581c66a081600066eba0b1bf195e16462af77610d6a151195830bd0d055bca6d1849483129a18ea91d797dfc1e0f6d07a3436d18c0c5f334a5c4aadf2e5056d19d0159c715e2cd650319d32667e302d8a9fef3af6a9545e0849f21a07796e59057adc42627a0a5f8c5ea318a090a49792860132f0fd5d4919ee50f200416a5a2af10acc58971f811402abf9047c9fccbabc6f83492467fb15148eefbf12e03cf4d5dc73c8b11cdc329719b4f8c52eaaf5815a0e933a84288d3b82bd6d386fbc4aeb2bc54ae70d67171c67f54db3f36baefa981e124737f1ba0695c790535ad233b152fa5892175ef3e3f23f536a8d0a6652c20e2e82576798cdbfcd649236dfc11538f345b405005ac1a5ccf6b49759d8798973e0df4e77e0c89fd098e8419abfe2f5896067246380154e1c9194733a0642df08c2e1298030c2cda913afccfd1f25e5bc92921e6e5271b9d827c85fe64ec46d1d6b1b7c7652c3fb8aea513e179568c8b9fae1064208d306916576137f0074229737030a22f693a6e1e27ba3a2799240cf04f21a4906b79794c495cc5a0c3172f64240935363b2a03b8a498403055b57fdcbe60e976d7a65534759f378a8367c72ca4e53cba16fb2fd4c269be99f9c0b95766e199b0cd0ea8195a74b08a789ef979eaff7f5b1f2d2304b25fe75959e4535e2842c510fd683ce6a57e9f88241a6f5e1c83b344d1e48e8eef6207f4c695a7160e42d9da2b737bfac00c86673a90cf1af4207b91965c1452df1cdbce7fcec877fe50ffb6145770c0185228a56decdab132677f095f41faa588913a7dbf6d99b9ebc195574350eb6ffec25f1454cb04f10bac2497afa7c07b1fe972a0d9a5130df5200d03cd4e2d8dd918eba724e04844ae0ef024e4d0816c80701c32d846e69b3d1099ab8d3f7a5bdc46fd4ebcfcf083126cd4f29058da7f6527510a56b2728c5a305a5890ea3956c25e5e70be606eed43f110c13e4e5c62a295060104eed434c290ccd370073feb94b0cb270833420422239240910eb8bf68d9e64695bae4ae47b0faa67b705e5dc498a3de1fea3e10d1f180ff73a45e47adec3bbfe0e6f74fc1508adca94c2940a080ed3201011cc8600a4587a4d7b5c118248d435316f6892b60e1c4cc049fed472eb006cabe94d3227a597107ee6ae46ac2b0495da4e2312bd2c2665f1e8f3b3af04bd11268ea793221028ac9a53af4de4469bcb38e6b7805a1bd4534282283baf404766e3aabc57e27eaafe6e3d526ff63ae74ad59ccaba350bdbc329066524225e4c83df6d4976b67c75a0fe0bf26ecb8917a001ff03e5182a561b20265f192fcc1e8913e9b604dd3a8c50dc6de4dcb715aa8c5ed6b1dee520849e74cf1e25c665e7ac8623d5786b551d0e83fdecb7a2e048918f795f25d9245e7c2b4b08639891fa08744c96c8ae610c5e00bbd4c00fb8c78cd5a56a6d9d1c3b91a8aa49d2bc9aa7487ee2675248afd559f2bf2e53241be700c20330aa98139849bbd25fa918c0d00ee715387f388e01ddafa0d9f32f535026c2a1d5e5ca1c7786f70297f692765b5c2060e003779ec678f25b0a8ad3f09003868e31d4ca1490751969755664bf9099ba09af48676c203ef6d893819fe75f9f080a9010cb0641d4108e4a034e1066aea62380082e9462d5dbd9763295152115d3d906e0f81405b67c03040f1fdba9e8a22291cee66a213da00a1a82939ca5585889443c582467dc98c1ec7db2ad0cc5675d6e57fdd11b0d261f95c9c493127173e9abb79820d59d5bc621de7d92f73eb795acc475365f9ec3e00f62de73e8aa17b0d82a2b0704b5f64f67f0a5bdf5b78ac8e7b56323104b97abfa18b8f618b6ff8ac515d30dc7e24688b5bc661dece854e144c2e6844fa0f73151bec43c707b8a904c1ba401150cf476ac133608609a2deeee4be71914f8e2dfa87d9eddab673fcefb8dcc149e3bd4398c3e21fe4f9c94a3a95b0af114e67e4d3256f6ca5769f8d030057a03c8392bee6c04094e843903fdeba4cb6b1200dbbf22b1c057b17d1fc74ebf0e2b948c48cfba494b57cf41d63076d21d63461940d91ad5c7e8b0b9119bd9f070029c395ffb2aa0e6a42c50428ab7b9e4601f855e3b2f5a517b47aaf4530d4c3703d108a6c9ef64f6333c26a8743f82a9de11622f91ba4df5453d0d8232a12f1807cdfe00795b0fce05816ae6000db48ff5a3b377cfc808ae6a34a1b751a2396709e981d204e448bf2944cf4eec8b08c07fe3e7d36d29378fe0aa213d1846767c0cbeda7d1d59676a17858927d239317a93d6abcbf2d85c35bf6887d987f8b1dc1ba48554530c6c891ae05d490a57578b9dd98d2f5ab56ef27e49d60fac56abd727f7d7d9b9edefd4b80adc1d26d96a02055030ae5b8b56f5370ac99312cb9490248fde5d277d0de526e0e8e4502190cd6bb86addadf33ce872838dff1bf67e8322c55d8f29137c13c8028fd4712a8c59ef5357e978b01bfd5de3caa9372195d51392e91f4e30a060d554050d0f5f69017511a188c84eed0eb705862f714268381e81321f0c2fd174a4c84063444dfe47ea087231e50acfe22137c25ce4d6bda02598f4d95b3b644a74e80a2dd2a9cbdfdc60db1cb3d9e81699915e8986c24a992b9c3c317cc4e2323de8a35c71a5b48b39196ae83ebaadc7b89c2614c187c4122b0d2003927f576ddf71f89698fcc08c9164a02dec95f7850e592dcc22c0d5532dafff7ed0586e92406009bf7252044ae37de2de248a0920419ccc9306243bd20d02984910cc33998f22c46a75e769312f46335cbe0839b44dc97f3a8dbe4c9b1eef663adf180e97f4dc8193376f7638a96512f0162bdf052c6f70b68712a9c52394216753b6e40d27875f59202d6169a3be15181c6820fdc55f5520a21feb402ad28a19292df4f84673c3e470e5f4bace0c17074649c80d78a2fceab1837af4286e0556a9b68e99edfda0f1b4d816edb9a17133b306fbd713cc57e73d7fbe4626e8a5db2e9ecefb436349c7c60683cfd01d8c292be9e7be832a93466e72662b06345508f78a1c229cad1d62974be787f3b43446e56c0ec14c7c1916909c59681f510273651cbbfd259282295fe30800d0abc96432b550df4b2cba15971768190a089a294e6395f7db1923b045ba49a9288344abde983e246667136886d1d6451c2e590f932f74a6919e95b43a356b38233212e30eb1ffa92a972031fabaa5992ff19f5e7adcfa424b76e1794930378c2ec7d417b0ba06c0995497b4bfb5193909852443a975b6f2f1eb35f76414bed66cdbb4d78127d777085949554cc066be0a389061a2612de4e3aff95ab471fca7edd7c325e46b6392741d0a8a6384393e1d88ee0ab0217ca4cdd7a540ebc592450a7ccefb601b787b8bb37f87acce0dcf05aa8dab9248671e0c4ae48e68be16c28e9fa7892392c327d920e7f228b1818c0fb6ce357e2a6f432a73b1625270de7598cfad78fcd4ed8cd61e0caeaea73e90025ad2263a0491cad774433a6a2a5603552d2432c3562df1960448cfc5c77e9600a342e5a0fb59d2d3c59a5cad80e7e89a6cde76404a5192b764aa2a5440ca4f2f53b36d1f973eb8a42f76cb8b85a96edbf9ede2a7e45e873caee089870a64d30154122507aef82763f71c119738f8e630ba183c3dad4bd7dcb22e154915ebed2d3049ad556433a3d477113a0d1ec2183cb40ecf9c7b2205a9d2313b9d284f881eaa5a0df0c779a62ec623fa625564c908819871d0eda6ebb87dae27cce9b217108c253412257762231fa826d1ca59d856d2422a3b70ad8db8f835e43facaa7517f412592fd56b55c738e15626155265f5e08a30f9e2380ff939680fd31d5c9818147d43dedd9a75c130e01343ebfcef42f34d9d58a395666159665edf5517a715e11370386245aaf38e979a6de8d004b021644af89e3a8af6a9895e15ffa5a62a85a2ce71fb035b791c43e9fc861c3fc0378cc445354c2aabe5fdc8fd6abc7e2dde7e824d41360f1c664770896d164a66d81f1c2099b852ac1b6231abccfa271554713725102613e592527272ee3de12a518a648515ab7bcbec838cba30bac22645dca6372e6823a08deebe71d5ea98807e9b3ceab9bcb3e98d3d21fb7bc97314374f8736eef149f81d7e60d6d08c3a748759a1e1b25f0d7aa34af578bd8d1909e5ee4dc730b341d2fa5b0f1e1c62d5491d963ef95bf6b074f471cd67bb3be21f25316306a63927aee248ea0e760d9e8991b53ae4344fefc91c6931964a25e70b13c0df04d755b1031a1bb9cc7c0cb893f033119f60ed7916b09a4744a7c4932f9d724b44a7274973743bd5ce061cfe78aaa07e08d9f7a69e8acd260ecd091a1ec7bd6bc2c86edeedea3839063635ccfc602b537edf32df8a6812eca9599b082cf34280289889631b9472c574029ebf387bdb6cbbcbc867e90519cf30e47224167b9eb6869fe71cd56741bfa44aa926f0373e0e53ffbc68d27fe5aaa2f7a7531407a65fe05dcc42adbbc2e15a6afd7a3c78e2f101fb8bc68725bd41f079ee5e2a5d465e57653e462f4cc3ec94e2a007b90caf34b0677c9e56aba559648b740822261f54ac4a38a21b66b734917e2db39284bad9731e58dbb666de753ab11f8121b935c281a326d7fe09dc225c0b5c0b5b0c315ba36d6f9671d83f86c238edc12e2a8ec4ef750cb0d3d098e85595ba077f002771dcab930abbce17624af0fc9803bb78964b8f69e83af710afe5d6cc8f47845633e779372fd4c378ae0a07544714bd1fab35ef607011c6d0928c03ac61f8bb16ef1689c63e520dd1e0061aa08502f49c527327715326a820d485cd07887a0d6e8adcaa8c1c4fb19ca753f2b4911717e8bf9d6de58b0ebfff6e1cb1c119116acfe1139ae905d532195621e8c7811b27a3237feb01af93b31c594c294f44db4b13d52caf64b91b28c85bdf10e3972e3fd306b16506999526687bffcc8b26ebae98d5458b10a56eadeb0359dafc771d5c821d5148fa5058ffd8ad8879ab993239dfa206269faa645b647f130ae331016c801d4200a4177566cc3d3838c45109a407b55de78941ffae8fe6177b1d1210644651ae0a77c1c7ab833a81a97e2b30766308b80045d91a5d4437f6c1b0bf205f4a40802c211764dac2ff9932cb07239480b1c0db1f67befdc739d191fcc40edb870842d4f014faae3b25722c93da7b5356ef9f59d394706fd9b4848e6cdd1f6d981d02c1e5830ae59799d597b732447560c47d82bd2a41ce7c3f119b064d10edee9a05fa19bba76589ab0deaf8706b80a7286618a2c66fe78004183c0fa91651a96260d0d914f3f8f3c43c77163254d0a09b3fc95cc11f92350ad2e33f6c169d8029d882d113f3fa27bcf5d4edadc971370be191b313598bf2649c391016007fb5174ccd52d7012d39d1d861a731cc45b296ba1f6845c912e4211782a09a2e82d38f190f8ffffab2e164ceb7a39337411ba9f57f9ea55d6f215a47a897bc2501d65e640c578dea884cd14c5c854f530741f7e45673d9391a3e40ef11f5080cd5e73f44a0a318bc94065bb85f4bc8bc8220b5dada187ffcac245a5fe5fcd61c0c2688914c38700447fc88588de588e942e4b73a32966af7b994d407f3bf2f1dc0d164711f06e29c2cde0f865e99b56e28f1c6d9b3a8d5976edc08efee5cc029959b87f456b940aff559971f91334860bbd138282f42369a3b9b1a298298201ca74935b9233330ad150343dae999e788a0914e33cd5dbc2125239ced1e0f922e57d857cdc852fbea618373df51bd7eb85d605c0765a81d70e07ff4870fb4512fa199f4529c894809df36783c415b4102b3671c9bfec49b039f6ec3af71a0420c9d9d7da74284cf82394e1fc3cda21c5a6f0b769bd841fcd4ec5cfdd94d18cd1ca649c949279e93aa7985fb1752a6c0535cdcd5290523cfb3b1e1675b9098341052cf410383a5991c77cb7c4c329dd77da3da6df0a086c596134a966dd14f71234e7b68f13b28313ec6e69d74f41532308f6582b6dd4090db9eef2e26b177391d074f90db2eea4083f25148f5585168aa5941b6086e78d1c5d7425329465ac9072d1d51448700c369ffafe578e56577af31dab3eb3581b151fb0c81435e9e451328ba5869ffc8b023bad9485a18be42d9b36f455a2fc8929b98b036257f6e3205b75ffb76fd5e259e348d4ba64aa2862c132fa015c12fe25fc56cbf76b370d51ba172bd0d5f4ac5f2fc2c1e9e33e5281ea25504a3121a5a9055fc28f59044702a750776c47fc301e959e462d940aa019d5d3e6993572d41848668f6687f3c30076bedcdf79e500dd3d6e39fb8dfb771e0c1f61d11a83ffd13deac03879d282d1751f5771ab8236b6303e5ca5ae50e12eaf2c64e02ad5aabe15ff5bbf2f7d20017165e21a1b3c39e90633fd4e52ffd25dabb3c2359a6bcae94177d3b069fb6632c0ec01b6dbd3c695bdce702564f350412c6c7826d036befcc5b1fb5f39735022780f41ab74fca8eb93edac43ce9fcee8979fd9c3fe80894878a0a75c89f962a2583188a91999edb980c6dc1c0b1cddbbfb0dc2f54c1831b97e1f2d58687fe2b0dfd7ed13545140103e66d9ef7bf01ab4c8d3b184295992a34f43683c66f373e29154b5ebcc4f8affbb07b63eae67d86c7e2a58f4c64b6c3e0d5f6e2e2f962860eb40f1c56f6b2cebe7e9af8b9b3ca86c23c3a7bd479a1df867a2dae4b41088c0e02a0e0688ee555038909a12383e469c8b5c8794a23e11b473776ff83ca8ec029961ad53a2b243dbcf6093ec89a9619606791535381bb773aa59d76a0bc0275", + "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e974750780303f2ad158d360e938540cb9fc2ecbc5168b0078e536f2a2c847cc131d440ec0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6e378e01efa1cb216ab0ced340b44360000000000000000000000000000000097621038a18ea02190913636079ece971a28590517ffbdf7ff33ba52157bce68c37c073712ba7f38d05290ebf864b98223de00e9e865ef8e388eeef184b6f39f6bc05eb3d0c54021d2345bd1729316ef17122a4b371cc1b35e37beb851894eee43c8b48d01f6e641ad9ba6e94da997fb154d92131d18a3d5e38ef60661e890f7ed9c3c630efffcd8e51ee7278f13333e167714b66b4e3e97a46578eceefc9955b033a18e6f8ec8add6f96a917a74414421b654b589d9ba8107e6007b5a8b71a20fb1deff0cf22904da6dd4303e7342972486fb2c422fc657a57fb1fa3501d8a94e68d15348674a20401c431592ca2aaf2dff57e20c39913382bfeaafbe4d2daf8171ebe9a277a845723db5482f9128cd" }, "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000043fcf4306e2131cf000000000000000000000000000000000000000000000000c5d67d4dda3528cbf000000000000000000000000000000000000000000000006060d23795c2aa33800000000000000000000000000000000000000000000000000028ba39f65b860000000000000000000000000000000000000000000000003431e382e17ac4ef70000000000000000000000000000000000000000000000021b4003913aea921900000000000000000000000000000000000000000000000c82799839439f89aa00000000000000000000000000000000000000000000000000012d6ba13cd4b700000000000000000000000000000000000000000000000080f5bc15f3c6fe10000000000000000000000000000000000000000000000004914047fa68bd1c0400000000000000000000000000000000000000000000000f6e01eb1aec7be2f20000000000000000000000000000000000000000000000000001f55f29a8b26d00000000000000000000000000000000000000000000000e1f18a47a78f5c09f000000000000000000000000000000000000000000000005849783f26c956b9c00000000000000000000000000000000000000000000000c40e8c3b7b05019240000000000000000000000000000000000000000000000000000b6043b2268b016ecbb30ebefecde0945bf712f33a5e35c605e9140717df8327bb6f5ef69445e0d2588d882879f74634b110eeeabd79da186e72e2bafbf2112924860dbcfdf3c026565a0460bc6cc51c6b1e502aa421fe4416359884492c4229e20ba422abdf41b2b5870384f803e511662e81bacbd584a2d20e25bd8fb8f0fa81166284ee76a1c0dba42cfa440d7ffb3747a6be1cdf331190bb4b1647db3ec65dd98881b1a2f2fe7de0b5b7b7e6fffc7e1dcd6d0a7f3fb92cd074ec90146d9824a3d2c3819ec2682e804a383289c2c9f1eba07cc60c1aff1cb5a6b70be9022d5a7a47e9f8e490c624154e85b5d4fd9b49acb44ca6ac73807ddab9dc20aea1648dd55a2a2cbe2112c1693d6efe05f2d2179bfe2b01d2d969c920d5f342d4a685d6be5b87b5efb1ee16d2b93917750a1dc3e00110ec67da2c9c33cc8ed7e6edef97f126e8ecff128ba7efe8208ae73c22be4d44ba04934379e8eb3fb1dc1c03bfa8b55a3486c5b131f1af9b8e575c05ed7f11c7f427c37e544ffd78db60c7f7b5bbd4da54d96fe002392dba277c15ecff2caa1cbbfa1c0972d054420c5d8655056de823a25f82d1772bcde199450e37df722a55e7dac41a8ee29f07fdf5e9977192943104a95800976f03432f34819ad3555a2bf5d72e9bc63f9d3dfaa9288bdffa426fa258f0e1e671b5758cebbb860ae076f5d73b5d244c234c00e8cb236ea18e6dfb7a6385a2ceb66fb20170a1f3e1904f20a2378844037b1dcadf863f5ef19937364dcc3540e2347c932b8fefb1ec54c8d8e9340e464e115efc55df0e7f56f24e44a8933ba080e285d212dfef11316c6a6f955b9ffca9161b565b183fe3b2819f5eaaada5c06755bf971610b6d70689ea43421cd65c25845da3eabbc145b5f325388497628019fb3cf9bea5175a846b9a4311676de776afdf3a762cc5f00fa4d0c4dfd6804110931e5e2644a05030d33c69fb9f202e3cd2df0341d476aa14e0b4a7d817d572d8b912a899d76b2fd4299063aeb8e49faad0c5f5ea9020cec394e6b0e1e3c9a0006fc8173d358061e3d7ecb78dea762d79411830ef656b635473a7cd16c21121ec372950bbc5726d72ee3f558b3757b55c96b953702a53078156f10783707ef2ac2f9753aa280f1268bcfc51d5de10121343500d12ba3c5f6230a09038cc8631212db3c1be0036f93a42f8a72b1b0f3a175fb9c09c4a31cd3a51d90a1226d6a07e82863c27680a6c050247fbced037c94c32038958b6ddd20115a7c942590c31e77dc1c3f5a6d558f0f0f3181a433a08f7641e3a95703b47e6efd448f0e74fe2d2120c5426c502b3d727e77f80277d8f26607705bd8bc705806a1269768f4c90da9c8d09b0bed9fa2ad2d1913c11030c9c9f9f60dee6cd28733e23e4cdc2717013c759281a65f9c3c13648b4b72430d9b7cb4dd11aa50988a436c113cc112f9042147a805128cf3d0524ab1eca70d990e510686f9d81900d21ea40c4eb8bb071309f1ff7af94fbb0f93e57f882e90bfd5d336d6cb45204d92fc5183aa2da5d92ffd7c5455bdb97f512e60e118641466f139a3926b97a57cec762416b5f039aa266c49d2811485d19e5b8c6d8481def5a7b11b4f6eb0d35a0a934ebdf8e6796f00e4fe8aa6c67a91450e430ec4c6ee2de98eab9780377cb932887af8f7a412aa11c46f63ca887cba374702c014f9181150ea66878ce0bc91126b160e63ad073b1d0ae14032feea6c05413985b0eb692a5b77e2cac56156ff2b5b27245c42913b138e299113f3df18768913b59f15bdefde9c8a535ccb394dc3fca88d77701e260e114f74e35904da232b3161239a94432fc6ed77075589e1d63633ee73427a601923d78b146367f15661597a3bf8bd841f53fdbadceda0f2eb3b9faa4a4351bb304131d0a0cfa4c3956c33343e3a8dec9782b0c12111165539ca255dfbf9a578194039d795df60cb57d147f56442d382c48b64b9cb458db43145a36ef77deedf05dda7c099ccfdbe61ac7687d4c38debfb0f94a862244b520fedc90482aa62521004cb00f3d7dd01f3535b2ebbb943ae0d3d88dba55e4ffcde1b929c4e7bf3f1061d9e25aa39cdfdbd3bb070a330577b342eb8115c2d615e373169af62a93283289bfdd959324811b0f3e3ddd44eb0b9c5cdd7eaae77acc35cbf66799bf6692402f5ef94af7bc25aedbc591d063a5e469a239ce962ad0f484a4e76fd2d4f6d152b35d0eb17c6421293d4c63741ab030a1001d888336e86508ab8b14883d12b771668e1bef33f067dcc1b3eff8730a7ae50bba456a0e0f88d3eb6d9cda64596a02fbc33b4961f1485700545c72d42e5d5f39ae5aa7ceaa64f38730de63b00e5491c72a5d315fa7e6b2f997fb8c32d691022ec660e40c3f79b2e4bb511be4f3e840d4a09bcdb6ef1f63ad37da6601137019a92853119af6d763fe02d9d5da4631b0b2fca10cdac6da89c06471694373ca09119b663aa8f2da003491e3592dd081f2c819ab7e65d912dafe80fbfe0b9d827b3a7d97730ca3997ac5dc303080a9f101be695a1ce3b315c5b47ffc36d829c402e443a78161c63bf19ebf7ebaa362fc71709980d1ed1134fef2ff9ce94dacb7164611513e3efeae83e976603751e74fd1cd0954090ba4339542c911dca7c1e551c1b621fa6e43c3f76af3194bde15ce001879243c4b5d2302612ea4faa1eced4812d0f8a6316825ce217dcbf7f12d932071cdb0dd595478abac873da51a48363ba1bbab6a7c9d33811c71548fd889f5d074019a5fd602e278f49d3a5d78c3eebfd9b15daf24b30eafbb9409820548b5c28d994930e37cfc0ee4f823768bc178f2c3c47c8b0c7f68e18197f4d1c0d2c14296bb2e2d8212c4e41962e429f66ae14fc0b1f5d953fb1a5f5178ad9c3457d20068064f3d23ce29df576e721b4aa8128f2a3a2e0989237b8b4dda0f89087c3252976c194886fdbff8fe106b82fe8cfe21ed4601b6e45d5e7df728f5df112599417f762f1b74d77d040a8ad0e44b935c58f2dfe77aa11f4d20ae469c70b1ea777211f3445db92471b04c84945c86a302d96cbee2ec5da3a441967d52df4ddf5d720fd042f565f49741b508380c4244af3ee17913bae8c9ab320459fb9e5ff6fdd0ac44f8ba61e9e6142cc8e5b29266617f01f913c653d122a01b954c49d601cdc2c5a06c02c720bcc7cf83013663183d11f1e87a2bae50b6521ded294e54bc8f029b1a9f11b22519e4cd52aa674780357b510cbad34face5080d1f612288c27c4291773537798a4fbd7dede6922cae8054535d163609a0d5daa8290c847b682a628798200861f4a7f155b7181b287cb8364a1c034a67e5af9031435f02ae78d0d2064184f366dfedc738ff7893eee8e5dcf0127805d813abcdc7b4a7377e53dcd006e0ac8ea735f93588ea0a71676f3c383859c9beb401c369dd53893251285121cfa07e3616b72cf7fb2edede8a60e8bae1b4d9406e467a75ed676f5c73081a4108b9f31a1dbb2d80e1b4692df14a9ce0f854190ec40a3769c1c738b3a430f5503b15ad4808444dab185ba8e4f56ded101f67a45dd451eadd2395c8c45e2726827ddef201bb70c9c3aa8a54c25d99e908c0ea19b5aa121567061312a5f07f059137b6f541daea8980948fb005bfa6d7f6e369095274292b312f91e641a8b9ea4225d759d940cb55f0a933e46a54d811891c1bf7affd9d037f0667dc58fefd5650fa96cda5692e496cce3fbfcaacf903dfdd71009e62766b6c87f3c5b7222108f0cf144bb82a02a2496ce587a7a23c89452f308e6792254e4d6d8c05b3b2ad825287a68259e53a09024f4f42b7e4c0154e6b67c580cbdb69aed8e412658585d9c16ae813774b518a16cb72e9273072f16fc1e696800108ab5ad68fefb495e92081e9d93ab4c44276fa03bd0ddc6e1cea3826569ca4be8df330d800d3159e9c7b004b02887ac694fdd3c3ca859ed6a55a0dc816f518cd898c788b530c2533c2933151b42e7b40ee1484f3d8bb497312cf4b8aa083dc199675cafde4aa229bb847506888be331d090e12335709ed8fa88d2a3abb1bcad1b9bc912b14163e117f2d6090e580d58e9762e20dc5745277328cbc4fc3e68b13cd5931d13973bcef490492da90e2511ba4f72bf9f5720e54499ce3c0a9bad2a88789682dfbd11fae3dc7f141c57158ac4354f912bd8f94543be5ed9144595eecf5d5b305d5a905aff23301585291ceaaefc89118816b3e053546b4974415cfe209a9ccbcf6b6d27f802e8193e0b015d0b91d9bbc7c16d3f673ec9ceb25f488495cbe9f65e7f64991628d81e4ab818a70c6a1f5c0344f972e3d2580118db18687ac9c61febcd613083e2fb10c96092cc963667d90fc249bb5f0246f77efaa9a624b54810f414a01f285ae91f5ad3517c22645f3a908264c6dff27a5fc3e83513e4dde1b2b4d06e7c0c21212a97c5faac94c396c3eb75609b19a9fdb04d70a404545b7688f3fd1664091e9e01d30bff59042e8eaa895df34bd4c8542de49f23b3b05247e2a60ea7c3746c5c1c82e8522d6fbac54bf45a7b88d466e117d6bbdb3bdb04404be881407c8b347128e4ac64753224c8518132ba80d90e9e36d0660a6420d8ae34883a2d3d3844f71aa13a859439f3ddf740e056cd16330f61f2f5f73491bae01256cc374555869e25aaaed0dd62f477ce2b9fa5dc14b4de837f1fc50e0012ff151c99326b80dc2f08ad72af198ecefe4a5bfc090511a7bd527573e9a71868ae2de0205e2fbec5972f2a267989ab9cefad432cd219000dc7f8ded53bb25544c5eac1c808f9ef87e91e356e43f116f8abf3db5af9c41eed862fb8bbb46ee67a9fe1d14c2b375a4d9c282a9272a09ce77f3a19320181606fa006e4809ead7028014bb9c0b6a272e0b02084794cb26bdb23c1c92e23962a8f80b9347562c76fb9782b08be8f480d4aea0d27502da998f8a4d9521fa0bd2ce332ff8d407451ee3189e4066870ee19128315f0c4621e1fbcbd85523b1b090cace6192c14beb904e071b3ee2c3a51322d9504dc793bce7dc0321b82cb0dec5c082cf850d14e22ef3af036646db4b3d03bb729ed41cb39eb0aceab2405ba0f571af49ff5b88af530fc40aa13614a82d4de4b0b1f3dda6e318d86ffc5181f1ae26c6720d6dfeecfc1d7d428bc80b38889edec04c86df292c6c2397538f796dd154e27f83c090a3918842c43fed2e008760a58004093996bfd42360e58e89a3a837a4f024d9ae4de6e61b9fde360f039ae79142310592732dc009d878c5e24931017ba6e1e127fc916cc487dca718eb0d207461accf461484e7f577dd7a7225398a1a222fbab01fa7b4cb966663decc6f86e911b213dd20e59ecbe262b4f78eed6229f8efcf1181f85bd54056cb9344cef882a0211f77acc433cbc72f660b53633076591101a5debd1c7bb75c477cf57136d912f1172c70849b5c0b2da382b36c1bdb2ec95fea0d963942e49a0bcbdc042fff82a017cc2a52d4e27bb55c769c4c8f25bf40863f646ce50b306e6fccaa3a2bef71bc6ef8cce6579234acca57828c774f693a58271dc056a44bbe3a5711292d86b1bc3f24eacac535dfa3ce20d18337450e14cae37fef5a82affb6bbc9048066902b1445ab1e06c254f4459768a63ee8f16062c8eae5d8eb828f3473f50bd1c4c716dac7dfa5ab8b4ce919e636dcf12a4014526e76d8876e9608df30f9576b5c03127e340a8eacc9bffa81a0a6ab84040c0c51cfbce8d0e898c8468a467c7482a3121bf8817f9896eeb0b803864731ec829f424f7ccec392768c521b4618a6ef3a1f864d74a4a1da3e2f0353e14d3341967d04dbd8b1cc7783f9be1937021b6d010604c7fa35db4d3f9e47cf064b7294832f18521aab04892489887ef26dfbcc3514a06665a91d11641cb70b9ebd9ab5998607a57970b30ed6e1abaa8d9a3d09fc0ba348500a8a70360967e7b2e6506caac7700bb250ae6352a52c80df145e23931ad6bba502bb84c26cc47683d959ab382234d2a6b50288c3a9f57be70087244c00cc036e2ac4050038c045f44e1174ccd51ae2831417eca2ae646ac4dc7aa0531250cc026492f0837dd16254415f01076dede1d0161d12a71091d973065b5b0927cd6e7b6887b563f8e8e38df1a2cf49ce127092ae7f14c16b82eb3bd7c287fe23c93e46dcfff700ddc4db7f0acce19981861fa8741b92a96a89f49d6a2b84641bbba385566e6db0023ca34ee25451f7835e0036587e3cbbfe5c5be17f68df7000bc03f38cbdc90bcf75475ed43684342333c98aa481bdc8933146ae091358ca2458e368f42eec2c19f1adc6c63c133152e072bea6ed1e1a254c040e1f2435ff27a5390a1f10b0afa9098d5b17e8ac84a75e0db6c0bb406d0e3e9c66caf5b32c215216ec3708f27dcb83202ae8c8b1d7832d6eb225a4ff6427d2ecd99fc1017a0e83fbe91a93c8d6ad1bcc9525d7ae40d181022456b13c271a213a7b99f0842317bc405b9565800369c7e9ffde1f1c955b9ee216700826fbee267429b2245e542e5b94f74d0e5d911fd6d861de60a9f9472adbb11091348f7ddd786bdaa3638f200fb4be2cc00aa4bb5754bc487d25643fab3e56ee92601b3d957d9969f0d8751295090f09b4524343335d83d5cb59cbe98b4b30e823fb810141a1db8b1f71032161af45ad8399c25773eef4135083c74ba34ed6e9b3f4ccd8ef4b54bc0db31c136bb4f01423231ec602e6867fb5dd7e2f864bfabd3389e116299ca683d0ba3d167f3caeb2a67f8b70fd97a454cac0decf9f04d6d0821e5304e95aafa64496b81308d84cc84554adff0428ac3e3a774a2bbf5adb3bc4bf670dd3bf87a76b1de22fbc0487db3499fdbfb393424d9c5cb42d832652a6fc484f44bf69307278f9482ab4dccf144cc70309b7d05d855221093137ccf6d29e1b92610d4100b5342a5e2400c944585d2dfff0536223c96d29a24d6b3e9c50c9110a0861c6a1ea7b03801af1f2fb4c61e033a13d3824df9251ea47f41739aa82911366142ecfb7cb757318759290a0356f8624ce873e0b73d1f241762cc941e31fb425c16da148efada514a1e93ec356d035d430e837d2bd1fed9ecfaf76e5cd05eef102799439bfd5fc20a5c7540491780488fdd25770f91535cdbe45cffd1a643d3393c2ff20ae2adb25398281111b51e2b65776c2d724c756ec35997aacdfcf38beb2283c693ab38709cbf556bad78501b91e4f1cc6091157d69450fc37a2063edf6e6a6bd6276915052ab24e1a353629dd9f290ea28da3f7c6267ea34a1e898bd279efead05314f611569998a8ffe0617162dedc7f4a8b77685228bd3665de049cd79924d0874a650138ed3fc5d669eab35ef5bdfe9bda7eaeff28c5432cc4520ff3d59c70407f6e23e3fc2315b7e8950f0d17e4845ba7a6beb01ec848c0a6fe91f99aa47653eac0088cd87378ceb2debee1216f0a3b8dcd699f2adab6bb3bdea59deb6af8b5c30c2bcb893d0ef10dab2ebc892846b6db33ddebb227231a17c6048fa74ee7a741b41b5a262d7db93dc96fe513e5dd0c99b6dd4642e88038d159980292b27b32186313dc8597e5cb810335e3af795e63d11907cab90e7d4c6ada30d42b000c6d438c296ac4d65d5e33bea8bbe7b4a5cd346524c4ba600f30a6847116b9488e4adf341a8d23847c4c67286d0c98fc0ddd2a38379a8b6876c063025972a9b15ffb456c11505c57c8339867f94cbead3d5abe1b40023239a3059b77874c2560f33907142fb27c921b1d03af6980a740462cfad06e7ffe76f8e5e12e59bb75ebeab5b420118fdd54b52424bbdcc621331f7c245bbc0d7b4eaa9d72e58fd40b21e71f558627f775a58502a5871380974b05b73b0ad148ecf99de8dda5c06ca5bd6e0604dc038ba19f12c8c3d2125db1afff20e968de85f6ab442adfc88efbfc9db2ab2c5b0f8fe1650a994f2ec40ae3bdc85a964f349c0529ad0f079f50ea0825ee21805a0c67bfbdd0693878a6f8cfce2ccd0b207f299e10855a7f85a30858d3f2b1d08f0f3084f6a16cae9dfeb648a27dadea1060b707ba1e5c391c47074d2eb57614d50dd5170d37488ff2196a83a2fbaef1b86bc62edbd5290f43132c00b2f3feee1c06717981ddcf1556a368ea88e0ed7e0917d701a94ff2094b41fd5095ee272d8e0dae2ee7650a04d96be40757148a898f64208cbfc043be8f4e78ed5412b9a3d91a777d7ffc8fa796977d83f8747557f7c2ecb1b55fc3ea284aa27e0266801ac81059c18e16b5403cd6a72e8ebed73f27cceba4f1acceae1bef11fdb318866a782eb7ac19600a29eb2526aaf66b2f2c19aebfb169b204ddea661bdb2496dd823f08fe40077c9fc0789e6b255fdf6f504c66dd5788a4cb0fb4b1cae352df34cf3916d124a7f654e6030d923dee25df8bb6ce458e8bba4013fa4a0753c0b468b7c72452a860b8530d332783d11a8dd68841893531fde317fffd7c48cf687d5a05281afb3feba15b0f7a4a93698c66bd046e45c1d45caf4bb242d757935cad8488650dc17c4121cd0a9a26482d62c2d8fa932d55d35bf2740b0f1fdbe7ac2cbd1a7603a8331f04d4514446be3dc504cc93bd9077b19049c7eb8caed8bc1b0b0d72db095a628fda749529e7e816ace5b180d4e1c4976f694e0753b94a26359535cbe525e672ba84b2828e9591ff60308d8269248299d7ce9a3167e391934ff6f9a9c20cb02b80acfd6c2fb601b493bdcf85c28279b3b4ff93701ca5e7c5769b08e6d7087a59ea9816c5d741e423901a061006ba0bcbb3d3e52efa26ae49010eac9aaf13d7c55acff3396b5e084135ed1a839d77aa5b2721adf8f82dd98fbaf497fe7c0cbac63bfa36f446fc0eac7e38904f6341e1492c908ea61b9aa45fc34c535f410a4aa69e11e6bf8db6f7c76a33ed651dbb031dd428abc9caaf72d3b11a5c4d90037aacf2ef9c52e3bb4c090181992129221a6d4f51aa2372e69135d49007397508ea07a57ad9f8d6a6cde315d1a3508867d8b981785965a935741b93fc077452283c34cb8f42bef83f16b50da40b9f7ad1284b86097f4282f311c8e411a1cefe1fe6884b6ad897c5a94e675537a7c72370316c5e81dc79ea6c02ab3f7f5db8d717e1b65dbf02a7e9952306d04bbe149ec0693d4056c54e314a3b1df47bf5e0522648282f42a97a0795bf818a7a5e6b23d809d2b50a22a938d1e6ca54a34f88ee030dec4280569f0c9de1b732b2abe849adc8d3d17c11c614b265f73dc91be83505d75421d8c36a800f9f36974aa23421ca49d4aef71609abc285a1a388fb32550df6ce80be66da801a4e7480d46ea0e5e9d99502f36262d9719d80a9e170d9041ae34238c1c869f7610cd75854b2d3ea32cb7b66688a40f32cfa8d3d810ab1a31e2a643bbb8a85309ee6719083301e02526877c00405e226642024902e68375d1756d87687fbbc344cbbf06fa33e6478df5777c0b315a6a12e78336ff36d99b4221d27f8f2fbe3120e79e072e4ccaf0bd39b96e0ad8adfbe35c8d8097e305a96278800c4551a5c5bb02ab81061b26bc39503fc116a4908b1c1bf3f4a376d504108d514ea4d9e7d05026332025cffba8052964a7f5483be52dd292d5ce87dba2810805b9afb131a08e22e92592637e9544cde42f374c6d19b68e40f55dfa0a3ae27a7731fb7ea0301e0afb15b1767576a8719a646806b834b16b9144d0f076e84232b782ed85f1606d4888008f51e9a11dd1a059e3d32195ce48e2f89484e51c713fd55ebe479825bbabbe4afc094fa4fa57cf797a7f2e68cfc1bbce7d6e16a0210a344b5d40ad3a696a7970363d758038e00d1daaf59ac26cebc9aa26cd5b70a08647d2b39f58ff5f90f97b7dd29a040695ef041b56e7b0ea2e11cd58fcdf9f913e24dd5bc513a9cbd5bdcf2fd9f2390dc816af21190b2ae286406c947c5e9230d1d620b0d6da9d45d5321959f1f1a68d3132d15a6d63c9db114544d286f36730dd550811f27bb81fa9d960323679af5998ff2ca35bb5bab129256929401b5ce06cf4a08a1283dbf1a17ffb32f605c7f867d59c984c19e8ee71745381ec3b1121c9ac918ddf281beca7e65b0a02edc0e093495dc39438b90b578952375c5c8282405c3591ae8b05087da8b63d02892949381a53538020303dccad2c8f354f7ee06f6be0bbc8a9e9ac94b62d7f17cca095ecdfb8d524e589d2526000552540a4517f20bf09584b3188f854be98e36c52df7ad7f2caac301238c6774d439069f6d0c4cffc8ff3314a5edac8b508dfd80c4344d62c58b20097c7f09c884a692aebf207a3f5d4f24dbc711adff884b4476692254bd1c65468ba9af6f09618269385513ef067edadd01488fd08961a820692dfa7c146fc564bee31298e99e70facf472ef5edd30ceccb6509bb6c8b36e0d6aa7caf7ae13d39b03da9de6fb75658c8c60fd102e741097da090dc4fbeebaaf20e09986fba5c2b0688563867ce694584f30aa5549e41a864dc76f9cb46e16965299daeb27097f37c9504b359225090e1cd2119a9239c21218bc5e41527be9e0af64fba84eef0d9ae42eac126a953d6266f03f936235b0a41aa1e90fa49144d65ace190e43c4bfac37f90b4ce7d2a7ae2e80cf3441b45c83689d54c8ea678b55f0371c606c92542a47ca79cabb2a848b4c328a8931ecba586c106bf80eedd0ebbf5513dacea7c721e55d87eb9e9fb2790301c5692614189de796e649f4b1cbe4bdb591ed37bdb60b2888b6dc97b317b90471c50b681838b84b418f86521a3bfa440f93cc750cbd018dee3af17f17dcf8839138e8ce7c1cdf4ea2e6a2d4c73306e3eafc10e1346d1f0d3207a0163319744e82a6cac3587e37bf0b715782e3857d1d33f9b7dae89d768d6099d67b4a8088fb9201d407146252d0c21eafd9064058b57bbef66a660600f7259af3daa9ae54f5917a25cbc22b509da7bcf61d7893916b49102547a36918dd295b364688e24a9f52236962b6d3c4d7f4c720884a66e17711a4c65980b32686e75c2aacf501218b92aa76ea1f17a36d4f4fa62390ae64055246707dd34102b2d669654060b47fd1520b9a97fec0e35191df2192fc484f18d9f1d033b1edd819c76c25387a53e41a2126555fd09f07ebc07017e914cca3ed428574d212d8972223c84850c8d6d4438127d41335492ab521fe12eaccc57d86f489b5986b47d556aa913fa797148c18516391060f38dc28602f6ba7264576da966e1e3bc223586feb02f4d7a46bc6c4426fb3c0a66226ef05c0fc7150e1f93cb7eccf3fc210ba79efdea61948d8d84762f1704114e8d214f766af5050c66c9f4c3650fb0f7f5e703cbb8a20f39cc39481cf7293fb5e42c3ec7f0bd54c722edc383368d99902a3789bb4d2cc813aad2ce045e4621b428b00a31fda45261c00b2d57021a3788742aa01e34669b39aff91507bb0f8c1bcef1a13c62df9de3e3b5321ce04d5d6a649b9146efa63dad75030003d368675c759adbb6257d93437c392fdd59014a8d36363b4465dca69eeb847c242b138e08fca95c703ca16fd5013a086a05b8da3d753ef76f973289a9040c11277d1d29bd91cc0cccd5e3dda88dc9b1da4e3ec1b9d3f19c4f3420d53c704a540d0fca299400df78af938387cd3de12006ee1490c43af52c6d41abf122d801fc1146380d3eb0b489b79b2837c4a32cc7819dab37fe77e61bc3c66d1740a3049413c249513975b8f2c14e2b4ba4dc0e100566cdd44216c3246d213e3b0794142a2fc271d302393b79f5537ab4b6e773c9bebb59d8905236d4ca9d9a4cd2261ace13bdfca0349619b573bf3d9d9472d6120011fbb42805e95f792ef416c40c48d12b7e6c243326b90b87565053d9af997addc85fab2342cbc33a4ab48e9e9d6e6c03ce103989b43389c03fb4372abc67b4b5a7f8e969f06714da30e6e2fd4abc970263d9361ef213f3895ccad00db689edf8953713689ff55a3d88857df96418f80f3b7b782cccfa70134468babdb4365264d3ee33520b528a69370b28b581c4ba1039bd7b1d3ea4092dcd8ba51d1dcf66b0e9c5f7bd65abbf46ec03a2c11ab4b313cc1555bf0198086250dab79aa284be53de9e97d8f77b10076d37fd2586fc0801aa92278061d4d74a4c2327bb0e9fb60054497f2069f3882111851e3bd98e111e8a74585acc65ffad0cc8a2f9636ada525e96aeb41555ec6d95cbc35c7f0b1126febf61577d397dfff012a8a2a2ba0bbc3e346bff4517ffb82bc00218f34a0524275f846dd37a83e31de414c7567d37eae0f205ad5b609b46fd3f15882aa39625ef68e343fe1a24d2afa1181036ada5b05326d9574ed1ac5b80afc7b4cc62b326f5b2dccbbf98d6035c4513c0035ee16e43d1103c8cc9c99854e67097385ab41d08e8e1679f1ac29a79ddf5f048e2fd4af504e440b8426a8701c50d3b75066325635a23c653a00d0cbdcf49e161630d2fc898097c0659ee6bc0b492436e179e126c48725bd68d0a137e650ce703d22705efb8b1b34c5541ad603758ab07b00c155cd691a5170fe43ecfd81acd5d83b73c43d34e7106ea5822943243c8dfb0aa01d8291a2c85f4f3736720a9a4a9fef7245e315b7f056877c2f0784f83c863d90f5a0e575230ef44d9eac80fcd8563f88fc421b32a71fd3bd992ab7040be637d1b7b96a48b6cc3e46a8f412726d81a0f71dda6f61e541015f334561f5ea8f5f50dffef49fc3bb61d6365d4f0656c4acaed1b22ef0f605a3692cf6b2c6409e7262fa5919b268d24cd518c7b0b0bfce0f52cff3430e3acc673b9bceef76dd8ca3712a3c9e0ec13298a1e14bc2c16552c1b2faf1738abb3f14310081c9a295bf210149ce61ab0005ba7b316fe0cc06fd183bffa67c9c2834087c4fce89f944d0052124c65c3174dfd325c017ac48b41beab336f002d852ded401311660139a3b2c811e80b92940a0645dbd238b5986f97c7db3f1d0d0f993227f8f228bf5185075e0d5ab73b10fe7baa0c11c493ee14857248fae31a5584856b343f37a12d52f2182c5146c72d2bf02051af5c1caa8422567fd95f517db9f10f39fc5708e54cc0701cbe6f4556b88bd2551025890388336b1ca7eea6384f5acffaccf30d6efe19c90798b7950ca9d224e0260b9e829c67e369a3516c71cbe7a61d6298d191c90e8d06016db8334d019006c5b96eb38758460acf69fc48db44c032ba9ab6ea87bd59041861986a2f2e00bc684b4d8fda3768075b515de0051cdffb880250b6a221f116f53c7d4176905f62466baf88878854e0362e08f6538c7d8cb15a831f7137db017037ad159d59e1d6c4d17e049240f95ebd7a1375b5a4180a57139fd3b5fd461911925edcb0c0f56ab046085a31edc068b9972e98847326d4e1deb6543879941a4c98a2773587830b03ff781ee11ee55c695af8b876740c8d9cea385c57026e211c03c6f56c393d5685c954b8f15c8c9029a742028af63e6740ef31f528fbf71859b1ae2adbd7e2c1dfbd021fca416afb91d90d39217329217bd19a1f594f980216a12c7b64900752db10e316a778868deda29979ddd21ea57ac38d5a1452e218fd6ad620ffba6ffb5167c3d4f7d0e032fa9a7fb13340190ef68792ed8d909f0902ef3cb391891ee86d04d881f95b67111ab6aec854e716aec6839c7c6ee6e02a64965300938e463bd80f81148e2866d598e6e843e9d4358e602b22660aa6060e9117b91aa7932002ccec550e9e7c58f74c39313db7a2c66bbdc21d53af0e3b246b2fe385873352f19df33b58771673063ca079315826db3d6d2b14c6c7472718a9605d5fea60dc7f671dbd7055fb2e6ee7ca651aadf1482295c13921ce1b3e26818e16427bb2bbd1045da1b8e49e4e3e7d9817026aa5d2f03157626b17a9532da63e4b959b21dd12829c5bde5822056de6e644fc2eacd2d993609bf22b78a50cc023a2f5df4f0ba7448064ce2ec6f5052631c99eab0c66dc208848840733f00e939dd6a730151fe93f7b22655f8c9c443658ba97d6a947809b32ed5510928a2cddc65f807eeaf756acea3fa946c9e05ecc26c68649567d47d66a701f552430214b8173ac6ff586f2754c73f34e024ba6ef907c6e2ec34d83d619980ff4b8e72ce1fb2401f60ba3dbb4d7320768f202bc80968a811ffbe41ee2c0e7e4e9f3071ff4e4f04c8de236dba3638547d57c459c8bdca8616a4a59761480700cadb369218200378fb4db8b25b0d7529f083090a7aa8fb2bf9d1da3e4e66fbf50e262080da0864fbc694139cd3b310bcf206d85b3b97f67cfc0e4455324b7ac1e3c1a711ef87c6b980c5c8cb471c6ac764008f859f06528ca70564c190dd411d45b2bf60a63b2fbf8e5bc7d4f60cf76822c199afeabac46ec638b571b6ffbff2eb203c72de58eb4634937f720fb70c8c37d9f18bdf0e4d18cdd4783aaef321bfd8943211f50104dc0501cfc8e9e2dc443c90290c063028dcf39fae0896f6359bf98c82d00aa9179e566810bf417ad5ee32a551ae96f0870b13dda21cdcbdc7563b320262ee77f55be8f856f5716082c82d13514e14795676ab6f46605b6f3935c3c740903761c60b3dcab05864ec30b350b993d26b3927579587bb91ecc1a9a4090fd832fe502368d2c16d5a190ef0ee84997e5a9878c73aa705283cdebb67a54298da22b886c69c65d60a6dd111d046c1b1c49be26d2e422beca6839024819dbfccf15027203792331b6a713413bd7d6ab58b6533367bae966942128d1b12eb87fb49c1b3ad1a1a34a37ec9e228ec871ece7a964aa11795bd40d1929260abf226aa97a", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed0000000000000000000000000000000020efbc1b4676f6018902362ead77e27d0000000000000000000000000000000060ec24b679943ff559895776f6f11c4429012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022aadc7250d71617ec5c2f852c2e49ed3180111ef6b130ece8b3504a7e2c3d0372c30f9e94afea47a8631132310e6d72615ccd3227fba92d2ae829172e7bf1c2d089943510caae41bc6adc5e284e6194b797027236225a98865400f364685fd6609bd127e0b1c620665f422cca7d6dbde8d84465e9268825c7e12feeaf3590216000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000616da7fc6a6d8cf29000000000000000000000000000000000000000000000009587bbfcd4668d960000000000000000000000000000000000000000000000006121d7f725ab890ec000000000000000000000000000000000000000000000000000254c3d3438de000000000000000000000000000000000000000000000000da6daa0c6a5d1b5ee00000000000000000000000000000000000000000000000704ad42ddbc8965a70000000000000000000000000000000000000000000000044dae6888c0469bbc000000000000000000000000000000000000000000000000000038249694578500000000000000000000000000000000000000000000000cbe544e1f83d0969a00000000000000000000000000000000000000000000000f022e5672b0d807a7000000000000000000000000000000000000000000000007ea76b9db2039b97e0000000000000000000000000000000000000000000000000002b08a8ad0db8d00000000000000000000000000000000000000000000000d86835de0e0ee2dd80000000000000000000000000000000000000000000000069601171ff6c2bd9200000000000000000000000000000000000000000000000dc64e0a8399e2eb41000000000000000000000000000000000000000000000000000122eb6e0ea81d1d0f557691796b92e9da20b93e97a0863ab1fe2ee78478842a0c49f45fc99fad1db541b4e5adbd88c9ddba68378f955a0fb8fee6c21ddfc8e2fd73d6ff009a283005d6958a7b2064dcf37a906df83f6046a1c41199c4fcc7b1de51bc48831aa306e4801d58d108d333a100de61c3a7c902d11c47b1f3c7d3a0b25a6ad72132132f09bb7f0634f4b826b2ba79aed873c3f63f0bb72625a7d02b4ccc92408ae597051f526e4e6e94fee05e2ae2d8e4b12904bda66ebd9c9d51e9961cfced09cbe11d880ce4fb8dbfbd641d0c0636c70c7c15bf3e232af15eba8b04b2cd763212bf230710d91859e6bf1de118a016a84a1174041993b37a39e1bc004c1b8da7c3b82b019a2526f1f013dbb5954649960459a4c506bf44865e1d6806947274b51dbd0045c746ab31b273af3beb543fb20fc9cfff80cbe7db0a0b511d2f9b14502ff12f5766c56461c016ee71291019c84e22c333cd16bf76310569b0871fdfc9d31b1da62e4933273fec94c2a3cc680ba04e9b6b233a0d13147367ded65fcd59d53406e9772bf6e4ac857b26dc812bf34a1db26c1b64a233b0260626c2469164115910b03889019586c0037c32ea2286fbe1bb31259f82545e8627c3b9e841226d212afe130cf4a9b76961d989066bbbeb66a469dc0b056c285083d68c76bc2ee0f10f7a0f8441de08dad090e4f7d846c46c311aa23fd6f18f312c62c8b007bd61472a2fa97e9e5e5abbf3e1351c8275791fb7d97b289da247a09935c9d149beb26a1d562f06793f41cf7b646a4e457adf890d6f0bbfd0ac668e53ac69d0eea63b8329280e22aad07e3aed7cb334e20e466b04dcd0ce6085feb9a6e975f08da975cd1d816b4b170d94dabe92ba65bc039266a2814b2a91ff6d9cd59dc8488720894a04abd5ba314a630e81139ae7217116133f998269c10294813aa4775e0b16da5a0d23169f49db334305c3cbad59b33c9c08ca8fa4c5ddc084767a3a1005877fad196bbf853f6103f56544f009d9ae15132acb6b48466e73ae67d8c56770d2b4131997c4345fb6896ba5378db26aa654c77a7115a54cbabcf7acc4d40bfd53de0211274d7054ac3355db0cc69e8800a1e98400c4f70a8c9de3d8dbe179b2d44da025d39a26672fc01702344e9a0658c0b20ebe5a51f8508146f73ff6e1cdf6c2e81cfe68d354f0b72533115a2703b6cc5f3f8dbf779cacfacd8680218727155f0b04ea4cbb18eeb93963d0f419b0b5fa441d4a833c7ad7b0d24b0349478764c6f62fe88e483fb87415b1e327ea905d32ba4ba50397fa0c70425ca096d2311c8b0b154558e2f8ca674b7f71ca9de0c8a93088d9d70a28cbe908285aa453e8379f8d0157cca15c75f28dbfc29e918d2f3a7725b901fd5c757339442272b8e3598d912ed8c10828fc7349d1da35ebaacc78ba86ea2feb693b2de08575d8ec8a80515b2678034c4cb3e15920bc6461a3eec15d9f7c19db25ac3641a2af7839540cd2561645682683c9e16381f3c7eda13f44ff9d6f7ad00a8396d92aea2ea28dea93040360a0de2b389e3b93340a145e97c0ad1a45e8aba6843a4499fbf9a31ad6dcab0dd9a87ea40990c30fc82076461d9f27e407bc86df402f7b38797805c4f388ea242a50f5afc343005ec4fa3d15f37349d5201d60204b3948860efb12aca466de2e07da20d2bee1ff4b716cea16ea40c035b6434208394ef47618c9a05b36b21e04420cee3f04ed87c1f371c2454a90ea9fa4981de27084d981ee935b3956f92c2d9d41e8c45932a7ef1260ee236797594bd3f2617f863d45bbb60820882fdad924cd6ca6c8bad6af9027a28bf69d90ede735d5fe94327977bf453aeb327cb99c16b1f87895cc16b0a86d88b40959d9afa7d5dcafd282cadd2eda3b6828fdd574098fc2bf635fea1852b1d69346c936fcd4e7f0c3073514acfcd9c7330ee361e81c01c84df3070615eb2ac30aeca2f379dbc4f11d734bfeb6529aec25a20b26fd29bb71d9e56779e362feb0c4f8e27798321341a8e8f3d35e67acdc479c642fba17ba87677f815f1f8d15b79cef7e2533f16ec1626277ffa230fd678c3c9b12160bf78e02f78a2d70981a87a4e711c1422fdd536bef214655ab3385b4ac07f1d329cc67e97f5e1c25d04d7d26dfb744efac8ba4787e1f213c4a09145b0bb93b6a04070876e3c7f4e4f0dffcc247525f02d8114e0c8b5228a51726ea1a26ee0855239ec8f411387f77f5cba25a65d6872cbb34a7aee156f43c09b0b5b48fecd1a312c270ca2b8b5482fd832f9099c6438547f93cbf674271b26c23ef795ef582e81a36c5cf6a2564e10891b4ef9b29fce765bbd37c0fe30c95a81c1362399c4116280a46ff3973b0b348f98fa741e42fb67e0a4139da4306469e1bd02d119d28e116127b2be924e581533e1a290e3fa117c0f04d0738dbc69707286e5abfa8ddc405400d1f696a80b5aab65c2e70b13f6ec3ecd951208f975b6c80078d8e484c770a8f69c998648ea766a404486515637864a0663b029c14f33a2bb8a60e2894f02c5c82c192c6c2bf9f20eed8e920b778b3af947df672a8275836e7dfed4cbf662ce92080ab808e197dc14b88d00cb6319b4da02c9807078e38467a7e8d310d9207d13c61a05df7bf7766e5c4e5094f91f3d6078666ff1b7f6a02035143dafaa501d47fc044bfb43d453a18f286f09a28b99c7ea1ca9a3c035e7f2807e8431b660048afe711b39e0ab2714be4cc16e02bbff9485034d9c622be8cac586ac080ac1d76b5418f677e6a83252f7940af0a5daa3eafa21b4c7f812c8d2bb376862d2b1479d741f8dd2b08a6971f6e1a1583b315f6467763dc2167787ccdfdb40672900fe95ce635ba0d06e4ff638f6574dead7ea0290934e40e43e210026b650ca20d0e2d75171be19878f9882d0d1810cc8b6f25156033f014b18333518a1e57cff4245a086e4c21a79596f44871a4665222d28d62ff5b82f73dadab1f20344e2a301233df15fb850fca4f43366886b60dfb0c148e43f2c8e5505761054f031e45992e530a393a66137414996546d93ad7b66a0a86b52f547548eda7c5e9895524b511e5a1c458aaae8d6634de11003153f68f16206876b45e7188e948c3f1daa18525f9048ecbf0206c2f818b3cd6c8f8cbf4f3f3f53408ae779b0a870b51792546102234ba05de1caa383cff3c80772d58d6bf23c4ab08750bf84cffa0e9bf020f19d6a7e461d8404d52ed1bfbb6eb292abe3359d175170898a2480005f9db1abd0a8a310ce3514a6db37e25d075b0f1a0822d78ffeefd3be094301f2c1c6febba07df3bc9e983118eb146c90b068a7c634fba08a71f7e5972f225187a78420bf21a477c7e545db56ad031125723ecf029261e9167e4ced9243047061111c9d89002c6f0d4c6238de97e12c43b89611e498ca6f6d8e2c1d7f04f3424399f4094ab04f76c11ac9856f9164971c2ed828ca4ad43e26981c1132b1289800c2e5f0d5e1c30ca44cdd118597ee0f62df250d260a1eb744d707b82b118b985f1383d477e12acc2957c37c491c396614b2ad84c3969ec1ec4f1c7581c3712a103196948442a6907783e4ddc2a1462e6b13a13f9e217f4faf0361ce35af4ce7b349199ca6208be62a56edd8e4c45d7da43d9fc5acb3ff2c3a1e8ffce95fbafa4da21ead9210ad32d381443055f187a6cb02bb24c4c702af499b4de82124499262dbda32d5000057871f9cc917de7237618da0246598a125df8149b1e1dfaefb350506237b8280517a3cd38ba02d278db56960f92e1cda3d667cce290550ef00cef68cb311c28d981009c19e0c73a175e1f8bc43e7f43177c46940bfb9629b3dc8bc2865f5711618c23138e3ed27b59899f18792d4f410e9cee41597751d50144a0efb7722424247808d0c3c7605a628f75f7b33cee58e4a298f8db7e3556a42de5a519523e19edfbd5551c2412ec0d91b21065eb911c75f0c6580f76fe4ae919dd9e20d90e04ebc9b1779aa7db5c235c15d4d62bb0a614c2ba0ae188bb90c08929ac3425be266e6c0828ac369fe62ef6e37377a346655bb964fcd07085830f8e31f66c247e04086671961a732d6e9824f107aa9cb66132da736c241a93091ce4a5d0328775161a9a5cd0a7510adcda72d5380a8e4261b99479379f9ed014bb36aaf09886352d1750492991f7b968fe80a366b41edbe23fc91064a7b4f8279975369a805a2011d4255af3918a920c857fecb08ca970c3bbc183f7af6cd4ccfbcfae498429d219e4de0ff7b638d612d8a932c88edbdaf91dcda4fadc0dacfb8fd6d8d7a6d5f117feb2e63aa7b37005690a183b7cedd096681e4f472b36b0371e87462f5ad693052dea498ff2ddcbd3bfa0fe4e996a06b9790c26f6870dfc3e18c88981d8f8c214fbbb4970ed74213912e132feedfd355ba5273b00d34bbfa521e5126ee83ac50c6018d032e9b1316bf07d5f6961424bb63041a2c28c55f1c913b15b637204b41fc084c1b1a31602652d016d9178eda06ee7128c09bd600a10d18fd526163f0b081047676d393073b466e12d85711f61038a03e57ab4882971ac99b0d6b2bc0e1b9f299e58527f0f16aaf6871e1db4ee3c5dcf014c2dc65524fb5ea32001e92b2956a53aac0a6512d222970ba9e533b4ef412f10c7eb6d34924f6793bc34685402cdebfa8dfc72a3322f3dc298df0470a4a14b65a2cbd0cde85b0b03915631702e5cf7319251934efdca0c802137e7622627764a0f62e35ddd4fc29ef4624f36221330a033a4d4e45bd32d0e7515516aafac2543bceaf4d88c822a2807c777662cad17ca5faf0c64b97712ae199859317feb0b7a23b9e486edeb09299f8ec8a22d1fd21e145147c31830b3d2a93407687ad92abd6b383db919cd235516e7b45214e830bf52047cffaa97e4fdbffd6cf7cf874d4e71d9ccb92d11867be2577f4d1b61347937a154f0694e2db859286dee84b79269d8b9916562b17ee7e0b103f7068e0ea2e5d9262d13e0e915ceae4c80138a00a157779751498bb2f8f0cff30015fd049acd6daa9fd01a052506bcbd467d78f415b6b30de2ae22f9927017a0c22917f82f9b9c05bf6547479aa55ae683aa7457eb8bfeda7a07e913307f4760440135b956996aded6319d9aa9abe0f76ed72488a3e22473ef0cde7c3736429108095e21cc0b47f3bbc9a0a1da00244efe410bdb1bd733792978b3e361144c3ec927c7cfe7b3fbc8c17738558bc2c90ce2ae06cd4ca92ec2f2ec62a5421a5148e62085eefb1a3092518429b7aeaaa7866b65ec7b74cf4e71696633f53f3ff622592a632e91d5af2332983ae86a98559f65d8f82edd814b7fdc257a8502ba0909071d670f16a22cc60642c30fb9fb7fc98474649efdd11acef790c632579810fbb80f8759feceb7061413e6df2f73cb040cdc5381d4b538866d384c44fae8b3ab330ff0b21753498352e5546550f9b114baea0ba201a86b12ad1d15d04ac3b3057507f7640a2cdb667262b3474a10ef58cf41d2ab58694bb562a2cd8feeab13fcdc1ae46053432cb7c5a671455ef7d8f7d8854fd13f9aad973eec42d9a27d2573162102b926891d0b8b81ff1ec89388965457c077b5e77106d056476a8d69cc035a2a6236cef525b4202ec9b49a7a24d33243c0fd4a57372970a46f7bbb685c8f2b2f02c81eedc45a4ba4ecb5a1601d9266daa5b594dd00dd8c5ac2c98bc6d1397001ccaca031b909879d6f36b49dce0e71c0a1f552d908dd7e18c37b2665cff1bb17fc40b5c7c46fe46827effb3dea634f7493abcaa3f21f974122b393ee3f97340a901c45b90fff46f3ee3a2b88256048094e6fc5d49b8ec566413d651b4bfd481f8bea6c3f158d26f2814a0a21165f5ce25ad588073f7747a302d40898305e5d0fbb41e6583ab90ccf4ade432ec1cf063ed319eeb2b6586abb670a4e8ab4f5d3207aaeb87540df8e47a12e79a8e73b6e9743aae8d5e55aae5da5fc551bdc8c5f150b1b55695d5420881d1aea8e1752398bec30da81de0b0601121045cd1dda9f19f7b5d2266d18c3df2b61d1b471737c2add361519680c8d0c3c151baa9a9c5619cd24214810f337d33af7a5438cb1cb419f4c2e4d5a0827a0e9cf91ff2947c6000ce6c8b213350402828339d996c44c2e7ac96b0452a829022fac28bf9c513e1701296727556febb60ef2acb478eb6d9a0e1eba99466e98aae13867404d352107d66c4708e57fafdc69de80c7817ae046f41cfb2882d3babbf5139a9ae4faa80bebdd2ad2e68bec5fcbbff945c5e7fdfe97bbc3093ba2b1f10c60af59cc60d80e5b6a0eb2cebee9e3537a4eb9f002c108a26738f9bde875b7749a07bac29cf527959a05a688c56c9d390a5dd346626ba04a0363646e6967033b9be18399be09288a4cb2e5a0fa4d656881bba6ed66180b6f4c0cede4ef98e34c5b0857bbc95b2f58bb87ee1c1fd91b5f887521033bc3155654bf8ac1a8c05cebc1f8be7e3a6917485cd6e95d67dc1c766117a3180fbbddaa099949c5bf1151c1cbefa10857b62e9d1116130f8343f772e091058db76e77567417464b0ff93d4585abed3886971f259252fa965f51868653047aee30d5e15c10cec90b2b99109910042dd655cb221cd2533b22397a15f8a4476ae6fd822077f56d3802f01d0b00aecca818ddca1f43544eb3cdf138b816ff5895940945597a7655e30b80c98616ef432c9da35323c7927924b4c1046711099315ec5d58169732b4b75b0a28bc279de6465a42b90ccf643157c4ba6eb5bcdcc7995ca022f0fe91c0eaf1fa910a775e32c70877ce29e5bde84a1189da233e3ace11f96364467be583eef1bfe52b1649a05a8bb44303db325b8fab5908df7878a508c6154937ffeea18cbf43fbf14200106941b8f42de76932b23e689995af9e3d04c0b37765389d1dbd1ad2fd720fb20bc864c025182b41e019aace2abcb7567da8a2449e1887286aeceaccdebf9dac751432f38e2e4ccdca3ac87ee7fad352d7ff41cf060d830a5568773f292d983ed3ea9c87b721b01df39b09be61d87dfac5dd5aee60dcdcfc8bb862c758500b189a324db3262f51d10e38f6d42a7fd9f051204231a06595de9ce681e9966313df7734363ffe1852b06dd157c08d974d7191399967c251acaea70c1376924e9082dd3d6eada315522ab87463acd7faba38e7e2f45677c6d6e2f93d64cd3ea57f855228b29086168d443b8768761cd92dce3ec5e7eb49ce0206c45b0d6c5633a8f7cfe5239b1d2ef1e141cc5a4c73ebd5827d91ea4feae63329ab93aaf49f8621647c7658eeac053de71515e765e1bf41d9e57fd7688cb8978b70edd88cc5f397c16e8bdae86806e7f6a5181704db97138fd3ad30bfe8f3f86cbb665f4a7de7916007e45137ad2edd56a1985f9a10a3df3458a734a9ecae9b3592e43bac49201782855b2155523019488b64b546a7aafc563a9b59f929ee0cf71f5faeb4ba027809a47ea91bc61156c1995627c4998f6ff7e8f59e552aac13a31d70e69b88bb179167f0ccad681dd077952dd05dd90a5cf24f2e5bc1e1915a996366fbd7e356358e0b92108bcc2fa60960482e8b422993ce58bfb7cd058f8e714f1e4d777dd4df6d94d185bb590e76ec3c2c47001f0726c38f0e9d2063f3ef67f0fb63023bd6617a9f53d4ebef1e0280a270fe757aac0af79058d909b8ae0361301fbf4252ec5b19dfcb88c9fb2e453ff90a2a56614dece9e3c3c8872a5116a230c48c7f987f257cda6cb8190c2d11786ae6a859b224093cfc916d924d1dffd0c72c60f04a7a4b525b9de3c0ad217f975e26a887400711a9ad9f1026afadfd263d3bf89dfed2e10939f0b3750912ca93f9498d6ef6f01d557fdb07a8e1bacb8f971f56788cc049b36ebc9944bc164843ed2f857d5b80c2d0e8a54340d6dc5ea6bd33dbf0197ac869cba99390d72ef15e01f0a8bcf394dae09bea03d0b6275f95009008923effce8d6c3fd71d7d05169b021ab02551c8061118dfe49ca2b249b80e1b31b35da1e0bb2986d8fde91e7e932c864120afc164374863379619a5f7f42a733faffb78e3c0307955c30f16aaa12541ee9edbdf1bbada45853c95bb84b6ae8b55727fecfd211aa981feb3195902cdf9c47b466eed7df5c99ed5c1a9986be4bff1f8ba0ca9028dca194a571ccdf56160d35f7db7b9b546457a3f58ee88f804b0af4c9d0ac2197afe5cdca827db53677c6c22cb123f66ed7c9a3a5d66631b4e2d28ec1ac2944dd8928c17b600462658fffb922dd59de1b5c4e3c596b135e5b3ec648c55487c4a4e9d03d0fb0eb1aaf689e5664411d678754c62a9c454cf433246204e7f0d35023465870ee1083f29e501695957c73780f0a97b7fedbc2a9d8659ee308637eeeef72467516b07db68fb78a9a9cd0d7e80ec1855e2370dc2a3fc5da2c00a6f2178d5ef6791a32ca0f2ab588997a20dc02b565f20a93ce84018b4f5e9e91b64f1b8fbb4451072010a3a6d25e1e1269b12967a77469f708063e9bb522a5f044414337ce58745a61bfbc7d14ec22abb3b904e2e7ef401b391fe764815648a73250f96d53b6291d924bd03aa9a5f58d5e893c14850fa85f01e939049ac3a56ace1b73fe06cf1bf4d1ae95413410c335e7dd27b9d0ae5db94283f75ff30557ee6ee8c2facd4fd1cd130640da57669309ad2ee076dcb7b4c485d0a68146b2a687a92c2645fde606d7e29fcd5920546f402de4ab466d0ee17e4dc11a167a17ecb77d8d34b4a67c18e1509ddf08b7a929b2aa92918a0032fa4dbcd8a735a34d11034f82e6f0c111a36490c0d9e41bb0b47ec8bbee1492876a765913a332f93bcc3dab8210c067becc09a0ad7426250cb94602452a54e1c073e2ca216a5f107ce4cfbb6b355d09bd65957285448bd1289902f5e9570e7aa2227117d4db540b9c97d3ed0f759545a81e4d1011380c194c5ea5db9507c00f61b87d20d1220fe4985e2e1d30065a23bd3156f23347b8df7af3454b8739d379debb9a5ab71dc50af6f215ec2a15792ba0450250004b392546927bd58a85055a05eadb613806d86914b6d62bffc79eccb75a7540a5b8d2e55bb8014d6618e71455a8f7b2f7f68dd008ba1cc25d355b90bc3ba1429fb08d96be9b5c21c7999f266cd03b43dd942ea09c55fb5bc11c6c5a174425c2a14c41fabff96d751937819c1ba20020918f9b381431322ef154b7363dd8ba90e7fec6e1072b896789f0f4822c0c147367c3fe82792879a0ab3caa19873431814955df92af2d824e8f1c3e879df0cc0d8a0f5bd76189cf2acc89a777aba170407d7788ecbb0c719fe9a150ad45a5dcc5d8d0a6923b7b27ef6c6b7d61c8d36551f74a4356e4dd1ebcef55bc96b0f6d4ec261619111bbbf7d01cfd4886d1de53623b33764960fed4f0310cf5bc7c27fbd4cc043bcb52e3e5a5855388cccac4c3e2326086873a0072e2bd03e12c78207d91b323343a2be00ca1d316015e6b7b9ae16dc2e72f35764059963da57573115adac29eaae415033e5500b11b392df0090240eedc5326ba3c4de726767dc7cbb6ce92f8cae01a136f39e72c82a7f455def1235497e0d5e3950864202a2f45edf6b800dc64b74b696f2aba673b08e4a2d822a929abc9e6ec8f99049f780d6281f48c9e92978df5d80724eb41681b3d4d2551149adab600d300405f0d78e43bd382f5011ce30554f420273618ed4474aeeba226d1c60827a4975c016ce132ac2bff671715085fa050d9512bc678279dabc7e2937ae6846d97736f3c49eb111407b8a9e9b127e5e2447d4901fc973a421ab3d22eb9ea67ff3094ae76e2adf6da4a0384008de2fd1fc52336fae0c0eb8a1f44109530dec9ba7bf4d1b3d51afae1f5d20392178cdb6f4122e1d9241d3d5b2630d22170047f6dfcf200ad991bccb04299c0400ae830abd161c6f16fd7f5ad7d55e2a91a91c308686453239d3a527c4b1761dfdcbee18db8f02e8efacf3628e99f32ee6e7244763d56e037832853423134a5f8a5641ae4f979e50ca4865ef06643f02fabe75f0cace0c0b039d4452ae0f365ff529891fa3f371e664b389ef7886eb3030b411db22fc59dfab6170e4a1ec4e136124a10896528963dfc5c96ed4259d1332ebc2b184c375fb6481b068abfa1c8d7b863b3c764d18dec95f55a13b807a1ddca78ef66c3f679f534b30384f7a796467db5f4e8595e0500d634744ca3ede0313c61ecc7c3c379c7edd4a9e711aac6dbb1768dd7604ad4ede42a037cda84d13a90845592175da3e3c72f9eff83911da8d0f3279fdb75c5057a140ad1feba217ff17582509379007b855924ad126cea015f23da97b9ff1a85d2e26178e7f492e38990677a0396678b98a389e5050c5a74ce4a769d0f445746bbff937f7c2032cfc6627c5c3af996ef37c2bc10c5a7c94c2c6ce88e04e466b9c01bcdaab60491533b97ef4a264153289662cee698be8ef26b021adcff3be3655a6de1f89d2271992a5af8f646b8d1d1887b122539bf5ff80b0d73282d288a0b7b1f6b15b7177151794da1af79b8a3265adbe34932c3a5ddc7cc991ae4280d005ed69ee78a1f612d55895627f1bbc0425d4f27aea7f2030c68852f2e2a869abc8c656282a69d91f4ce47b4ac4253caf925dd52ff54d1cc443761800deab6659b954208a22c27c09d9bc509923b5af45933add7cd707235c987f02bc0f609b1ca5a831eb61ae990d69687bbcbd8889eeab15fba07d77666bac222248c9a11092cfa1368f7ad93e2c70a325f6f55bfb9c2714850e968c5f9159bbdc4da5b5845ab1f4b196b06b860c8eca51ef7ee7ea1c5ac6741c3b45eeddfa510a3b35c87046b84859d192a7482e3c7825d060b1598765bead1f4993717136208fa56b48f2ce52669d7c2aff3d0bbd8cf1549ca6332ec3df67c16d599ebeba774d36a3c23e47fb3893ba793a9a2825bf3dcd7dfde5259eabdc59b130726508bf5285e8e462de85cc1fce521dd40ba15d9af05a686ca5bc521d4f87492904e9471a27df178a9c32b4cd292debb3083e36159fbd062777ce3a5cf7e42b68cfe357293dccb10a66ad7605de413bb62b339778547ae76ac7a2e1ed9831ef465da55fa64c661288a01252ecd7b9f5a62e5b8687ec4064245e7c14f1b044bfb5833f5f5354a5e52a118f30523e80fef11c4a642b47eabd6c27c18e8834c0aa44ee3353aeca611d41d5a139741f106ea329f5a4ca9b810877f6a0530f157229cbd931de9be6ab060c9c7aedf12a6d31bd1416ac65e49f6fc1ba4fcc40934eed1ed6750feab533cee8d2a5bf77b70860230d4bcc80e332fd6dc8eaa597a64910504bb9f7a5951e0b1cc5549b084e62d3612f42240d9fe81c6c474f9c91184edd5d3d9b5b9211e72d85f6d4307f89b015d01987a2ba529b3e3de4305c8d91a646ccc82a4bedb2a4907e9159c7c99ad0ff59036d6623b151c78f80b23febf74f81d0a92495ef3b64f19e66ed2f41012533980476096427924dc67d52afd687936db302e8bd625ceac02ee8d96da3ffaaf18d0dba69b142c534b2af9d4c54d09bd072b0abf5dd514fc97ba8887ba0a3cd11601669661d81e87b4a811cb36b178dd20b80a0f2a7d30e3cf1be317eca455d578327e0b8517954ae519d46c43b511ff7b5b5faec58ba349aaf5e9869f04c2c67a30474deb6a73b2ebd5ac1134811f1a36c4b25196a574cc0466bae6b3fcdfa67a40beb8730f67992873081dd969bcb5fc718be38b7f2d905e0a956005ef56acc7229696bbf0b5783493ae613da7d540be2d78baa3c0ae8ef5dad872e5ddde1b4801ab09d3a847d4e7b763d045842143da7dd7506cfea1768cf8f6c02498045a5c12e7e1694b77217b83e4398daa91eede9dfd0e756b5454f4a53c44db1ed78b05f180801c4a0e6ecd9dd3c75364074acb511c8c2388bda38e4082a2f52d456d690044ac311feb298ab6d7975886247df78d1f764d91992ed87babb126fc4260470063cc176948b6830efede55d9dd95863e7000b0d6449779f26cc2fe918cbf16b0b156dc79e15d687a3ceb7f9e89f387efa6739bc8694a0663c4ddd1034e732d82d98654f4ab727422694f967ff95c25cdcfb0a7b67a9ddbdb38b02ff739702532bf9776901d83b68bcd265e6b441044b18a466db1744e60f715828741c53b38c2234f0062231eb92cbd23949615f9564eeca7e3d36a66eb8379ef32d66fadfa4142727380dc5ad292c52c7a073b76f8cd789835175390cd2e01c49670d4d66062ff44e2d186ace9c739f41b6027976d14bfc760f07d82c033b950d9239f066c31719f4e87afcdaf9ee8d0278b99a0e3d18849003d3be00c682b37ccc6095110605f89adb2b7f013fce5b354fb68b2b32577fbd6c48a89bc1217c32ba2023b975219cee35356409db3827f407d60777d58e8348a4b165a09faa7e89660e811f2b093603ded70cd3e08e680830d676175ecd7efe2b0425ec8d5d7927f40d04ad5902b02397c2b75482dafe445739acb0e8ac4e53158ebd6e15185513efe9d06f38251951521b1b37ee05c9ecf7335e1d561617b68823ee6ff207eecb139bfaf3c919fb94c51769dd2fcc89cceb51856e10d9739407ced8a9849efdc2411496ccd72ba430028754d7f3e1d7aa46a8a78e0a0b05709edb0612241c0431cb38d32a8c05fba833cbbe1077cbb83aced24898c91227ae2ebb7c185a8d7be7353f8a0cfa2b1e7248dc2974a6e373f4c716681a6f5067fe446777c3d31571ef38e09783501336d31654c7c5d95a34d5671e1079566c33d4f32cc5195d4a999f56be81b1c9041c23e40a59a05ba40da1f97eaa2f83d737c4ee7767a7f75d44f94b08c838cb26c9c73e59f573dd389732d09fd9fe9eae468c6b879ae3d2a9d2afc712a9330a21f7670683303dee05ccfc96beb8f2d894df04693179e2973cef115c09354775010f3c5156faba3e58bbf7bd54bee5e5308a25e94fe3b1147078b6147c753db4288927e082ab365f6cd4f74d0e850a3afcf01629f899df35a216e1d3adfcbecc0b5176ab93d7221145c6d3f2011852b1b0d5e52595b97c6d75e6551b6c018464068a4250a21e45c570aef973ff978e2d0c3d1f32ca88295e8dad9a6b665faa3700a8ff8659e594a167530a95f3f4327d7210e6b5963fc3a3444d16cf05a888dc17cd53ef517b3ca9f2c4da57eea412b405d27341a8f3da610420f8cff8fabcc22ea7167258ec2e0e0153836c5e3fcf168f25aa7172528b7a7a10e6a961520d320657abd3feb900a60de5d920a78a84b6b91bd86a032714f0729541468bc628b42fb2580aa35a66684f00e8aa78ccea16a6e2c2a76962a2c97d2bfdc28c1a14b1029c7b68587d6cec18c23893dfb394fa183bfe597d4d9f41afc074c0eff30ee90ac83636244fc935d52d9f8c32884d8ff049e073a3dbfe2e7f3a9fabe056db851b8a0ab427bd70186dac196be85cdd9cf6a472db35588775a41d8f5bcf95cb1d2a0a34567b854f3d22327387c2b32d30ab6abce341820cb40065f24e9b4bf7a111bc7a424546ad33f509901289798a8b7978dfb8db35dce1f6b3dba62a1c5ecd1b0b6ebca55130fff8bf52b3f525c4b8b490664fccc47262ff3df8b6042652960484ce932170e3593575f723508e98ee25bfd80c7c9d432d952dcd2eca28ab9b2294cc39122d4d6c0eb32e53e70a7e8036c79c0c0dbd3799259c82cfc1c7d0910d399780e2969a47f912e8b716239eede7888a4f541bb71f30133851e10cafa40d3e8226743bae7711324bc4ec129c85002d3083a2af215f38359d37b8d6ece90f6c000b0d56175158febef5a203c3fbf701312bf3d11667ba4c0409642de183162b2a7fd33f67c8f2c4df85c2749615563b0a782dd38927c24f0adb3dcd67a82c8d98745e75c91c8cb14a48a18cdc21c337d33defe7a34ee52d58f2ecb0f33e1b2f7ace0a364d7b999319eac3e703147b0f0289d13b496a5baf1132e2d69fea1c05498e6412f68f8157fd373a87e35aa4e60aed1ca9035054dc4624cdc293e6106ab5b7636b1eb293919de2f9cfa94f8106140a2fede50519fec44ee3b4d12a01f74160bfbe701af451915e10e836bf9dada21bcde42f5c5f62adcb5ddf571b2ed223c42e6671814eb85aabd89968c5d0fd5cd71293c45f665190f6dc4e71161bdb143eae947ac64357958b27ebfd7b7d747c635220135d07e5dff8c97c02671cc1498962fd55a7444e856319b117c443a4ede613035f4771414a54e3a189b7030d05c4fed5241d83bcbb4bfde04683b10d845642ea853252e0588d953592541017b5276b6057e49402142dff4a01b46e0577963f1d46395fdf2d11092f0ffe15681c8e2c65ed60de0c5a4fb3be87a9ae7795bbfe16c96f2a2fe963c12e7eea0f851773e33fd8eba81fcbd8a3f51081726eabd522109b504e688c1367f7a831019103d297cdd5e7d383882d43a1e392dde72d8b45f74206cb3a4af9dd534e881a1c9c5304e1951778074af2513419fcda57d4002f8ee3fa959e69394fe07757026d016f186a85c5dcf4736885598c99220b8e32c16c41fa25e9757c796abf710612a3fdac0fd88526f8fffc53774f41d7d5b985ed1af86b398aed2074aad96d09cd2356845017140ee303a82f21315d018a149cb23f4098c5096334152791f128495dec89751238d8f03030911d3d2975e8c637bc65a3c308ee81ee28022f001befbd0c957929a6cf5e6682b645d001a3ff9a5a118325a69b543d179f878b2d0c7808b8477eff26b1c176fd463289dddecf4765b048bd6cb82468bd00c5fb42", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e44107940cf3c3a18e9c2b8af0fc91a18c91a709c00a8093bbe606a2e9fd472457bc00000000000000000000000000000000c6e378e01efa1cb216ab0ced340b44360000000000000000000000000000000097621038a18ea02190913636079ece970b3f37898c93078120c11921f007446ceef9867e6da4e341fb3bf8991edcbd630000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000223de00e9e865ef8e388eeef184b6f39f6bc05eb3d0c54021d2345bd1729316ef17122a4b371cc1b35e37beb851894eee43c8b48d01f6e641ad9ba6e94da997fb167714b66b4e3e97a46578eceefc9955b033a18e6f8ec8add6f96a917a74414421b654b589d9ba8107e6007b5a8b71a20fb1deff0cf22904da6dd4303e734297000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } }, diff --git a/circuits/benchmarks/results_secure_agg/integration_summary.json b/circuits/benchmarks/results_secure_agg/integration_summary.json index e3c16fd88..9466c36d5 100644 --- a/circuits/benchmarks/results_secure_agg/integration_summary.json +++ b/circuits/benchmarks/results_secure_agg/integration_summary.json @@ -24,150 +24,150 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.612713375, + "avg_seconds": 0.036223306, "runs": 3, - "total_seconds": 1.838140126 + "total_seconds": 0.108669918 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 2.183483124, + "avg_seconds": 0.156799111, "runs": 3, - "total_seconds": 6.550449374 + "total_seconds": 0.470397334 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 1.943779167, + "avg_seconds": 0.234711584, "runs": 1, - "total_seconds": 1.943779167 + "total_seconds": 0.234711584 }, { "name": "GenEsiSss", - "avg_seconds": 0.804676139, + "avg_seconds": 0.220404125, "runs": 3, - "total_seconds": 2.414028417 + "total_seconds": 0.661212376 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 1.468442666, + "avg_seconds": 0.202163278, "runs": 3, - "total_seconds": 4.405328 + "total_seconds": 0.606489834 }, { "name": "NodeDkgFold/c2ab_fold", - "avg_seconds": 7.19246175, + "avg_seconds": 7.089881625, "runs": 3, - "total_seconds": 21.577385251 + "total_seconds": 21.269644876 }, { "name": "NodeDkgFold/c3a_fold", - "avg_seconds": 51.025217958, + "avg_seconds": 57.963919402, "runs": 3, - "total_seconds": 153.075653875 + "total_seconds": 173.891758208 }, { "name": "NodeDkgFold/c3ab_fold", - "avg_seconds": 6.830379416, + "avg_seconds": 6.906346736, "runs": 3, - "total_seconds": 20.49113825 + "total_seconds": 20.719040208 }, { "name": "NodeDkgFold/c3b_fold", - "avg_seconds": 55.386840874, + "avg_seconds": 50.328348527, "runs": 3, - "total_seconds": 166.160522624 + "total_seconds": 150.985045583 }, { "name": "NodeDkgFold/c4ab_fold", - "avg_seconds": 8.455168041, + "avg_seconds": 8.440406458, "runs": 3, - "total_seconds": 25.365504124 + "total_seconds": 25.321219375 }, { "name": "NodeDkgFold/node_fold", - "avg_seconds": 16.317384, + "avg_seconds": 15.28584993, "runs": 3, - "total_seconds": 48.952152 + "total_seconds": 45.857549792 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 19.080945417, + "avg_seconds": 2.887353333, "runs": 1, - "total_seconds": 19.080945417 + "total_seconds": 2.887353333 }, { "name": "ZkDecryptionAggregation", - "avg_seconds": 50.556414917, + "avg_seconds": 47.438696625, "runs": 1, - "total_seconds": 50.556414917 + "total_seconds": 47.438696625 }, { "name": "ZkDkgAggregation", - "avg_seconds": 21.026504167, + "avg_seconds": 19.61153325, "runs": 1, - "total_seconds": 21.026504167 + "total_seconds": 19.61153325 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 52.175752756, + "avg_seconds": 22.058676235, "runs": 6, - "total_seconds": 313.054516541 + "total_seconds": 132.352057415 }, { "name": "ZkNodeDkgFold", - "avg_seconds": 145.216893416, + "avg_seconds": 146.017955028, "runs": 3, - "total_seconds": 435.65068025 + "total_seconds": 438.053865084 }, { "name": "ZkPkAggregation", - "avg_seconds": 105.563541209, + "avg_seconds": 24.53227325, "runs": 1, - "total_seconds": 105.563541209 + "total_seconds": 24.53227325 }, { "name": "ZkPkBfv", - "avg_seconds": 5.966218292, + "avg_seconds": 3.559760472, "runs": 3, - "total_seconds": 17.898654876 + "total_seconds": 10.679281417 }, { "name": "ZkPkGeneration", - "avg_seconds": 379.488959389, + "avg_seconds": 70.937173541, "runs": 3, - "total_seconds": 1138.466878167 + "total_seconds": 212.811520625 }, { "name": "ZkShareComputation", - "avg_seconds": 101.477507611, + "avg_seconds": 36.93532184, "runs": 6, - "total_seconds": 608.865045668 + "total_seconds": 221.611931041 }, { "name": "ZkShareEncryption", - "avg_seconds": 288.672301658, + "avg_seconds": 116.868327526, "runs": 36, - "total_seconds": 10392.202859709 + "total_seconds": 4207.259790957 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 305.775330152, + "avg_seconds": 106.505539597, "runs": 3, - "total_seconds": 917.325990458 + "total_seconds": 319.516618791 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.114692638, + "avg_seconds": 0.12560836, "runs": 3, - "total_seconds": 0.344077916 + "total_seconds": 0.376825082 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.34096735, + "avg_seconds": 0.320742583, "runs": 5, - "total_seconds": 1.704836751 + "total_seconds": 1.603712916 } ], - "operation_timings_total_seconds": 14474.515027254, + "operation_timings_total_seconds": 6078.861198874, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { @@ -177,68 +177,68 @@ }, { "label": "Setup completed", - "seconds": 3.246619875, + "seconds": 2.661124958, "metric": "wall_clock" }, { "label": "Committee Setup Completed", - "seconds": 20.239166083, + "seconds": 20.166480416, "metric": "wall_clock" }, { "label": "Committee Finalization Complete", - "seconds": 0.004966542, + "seconds": 0.0012645, "metric": "wall_clock" }, { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 157.906869, + "seconds": 162.089313, "metric": "wall_clock" }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 1350.540072166, + "seconds": 604.932522792, "metric": "wall_clock" }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 1357.853179958, + "seconds": 605.453073833, "metric": "wall_clock" }, { "label": "Application CT Gen", - "seconds": 7.889468042, + "seconds": 0.353204792, "metric": "wall_clock" }, { "label": "Running FHE Application", - "seconds": 0.0710175, + "seconds": 0.000809292, "metric": "wall_clock" }, { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 70.07983, + "seconds": 50.493282, "metric": "wall_clock" }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 389.344160916, + "seconds": 159.9251575, "metric": "wall_clock" }, { "label": "Entire Test", - "seconds": 1778.650297542, + "seconds": 788.561521291, "metric": "wall_clock" } ], "folded_artifacts": { "dkg_aggregator": { - "proof_hex": "0x00000000000000000000000000000000000000000000000be4031062af5469ba000000000000000000000000000000000000000000000000c2b34be503df778500000000000000000000000000000000000000000000000d7e8ac169d44cd819000000000000000000000000000000000000000000000000000226e9f937dc8400000000000000000000000000000000000000000000000eb2c7b5a6fbbfe202000000000000000000000000000000000000000000000007e63ce51619b2b03a00000000000000000000000000000000000000000000000cd2bc066f5bfb7eb0000000000000000000000000000000000000000000000000000180a275d95616000000000000000000000000000000000000000000000001588f71ba712f91cf00000000000000000000000000000000000000000000000f5e274bc9e3112d9700000000000000000000000000000000000000000000000ce2900e6f8a2b3ea70000000000000000000000000000000000000000000000000001d450a0feb34a00000000000000000000000000000000000000000000000893eef95a46af9b6700000000000000000000000000000000000000000000000610ea1690987e94780000000000000000000000000000000000000000000000026773ee1ccae29e350000000000000000000000000000000000000000000000000000f7de5dd648ef04d5fc40bfcb431dfd8b2fe77b6946e66eb7a8691ab31b9ad300d29366030dc721ad9f08fd9c1205bb5a208829de6f760b8cfa7311a3d4e9012cc8357b8d6cb9099a4a7906202912663409413b8f95db686885add4bec427fda8a078948d4d5628aa9359e286cfd9e938616af59b08fe7e0ddb1670ede6046a1c0c58e62575dd064b6290f765e9ffe9947c9677e07f369d130a04e51b428ebe31ee0728043fbd0202992904bb4d2cb55f641534bdab7fbf513e59fc0fd094a50116faf966cd71266144d996fb80a1d67ebfc31c554736779b58eabe68a9b77e288d8de098bb6825d67e8f8af229c06cda9a9e53c6dbdb50d895d4a469a46daa56f6b37a7895131925d6f35fcc62ab791e2d95d20ec944f9f2a744da733c1d2ff4ec7742d7ccb809c38965d7317c3eae99030b673c6b7311bd610393138b0b54fdacef3f7dcbf41d5858342b0f9a0496cf06e00856a1c613c46164b150eac569b07e31bdb1f5c50292ba9b7110cc200cba66dacb3700ca2b93cdfbcd99547807cad9a6778416780c0b613383c0e7b7687df425fbeee700961f6e6f8f1cd8edbe2ded521cadd5972e2b63224fd73010db4b74ed0f3c7fbec0e1c78c7c30ae38e3fc7d58c8096e64048dfb1400bb0cb58ea4adae69d667fe92b899b921a5dd31783f26145dee63242ad083e6fdcc90989d2fdf57b4c9ec187315d9e8cb8ed50584fca7fdae08fbfd2d34411db2b5c57b6991d03f887123ab0fd0a5c450c48e39357a053112c62f992f8176cf1c648f258750d0a47d97212b450cbab15c3e0060d71fec9b39a7bf96280ba3ebb516bbb4f3c8f180026ba77e7a1fa6a64481c98c5b6f93781c69be3a23ad733505ea5bb254d533dd64fa7a8634a18f0b51523739e32a37ca9e9919a106dde1b8992f2fabfc973b5f38d7d3b1813220d03c4f3dc1a094dd72bc5b2b5b06cabe3e4f4af547834694b5aee1c436ac8bdbb8a84625ad4a9cff3ce92b824416d14176d0fca6bc452e100b2415aeceec371d150c69c78d1af5bf854d28fe8d0e10789e44f8e549421d9e46bc48990aa34a13b16c579e6c8953262c8ec9848006371b9b26485ca867806e2305043527f33c33b2e96c25acdcc69fcb04005ff52d17668a477bcdfb06db62e47445cda78b5cb6094f99cc644ae2f97cb48587af2403fd78d171398769e6386a60ec0c1aeb0a80bcb74f33f334387a65d27e233e0a989c1bf2cb6b7f44ac3665aa5c0b0f13e6fc6653d22c64e2493b2a2c74bbb704e181e8b3a2053a43b7556861d49fc25383c3c7cca711c753082fe51d939e6d1fb4f93e8b2e2d170363468586d5c3077658532578c9a139e0c0212ed1828504181b7790dc3cf4b61e7f986479673f74fc9f5881065cbf09321ad9d4851c9f070b98da60896bd3e085d80a970ec25538045f990b396ae1344ecea124ceb934a110b05412c6cafe9c6c7aba2cf8bb0a5b6220ac3ea6c2fcb41396ce23ae5d12ec15fadb44fd24e645726cb35b80b4f814500fe91de9f610fa9c2fa48a75737ac721329c7803475ba0ac9b7a68162ba81c2100c1b1ddbcb8da202325304f861d7a0ed2dd1b91db742084da868504df566366ce735028525a0ee60036b35217dc2f08e23dccc9ab6f4868ff67f443a726b1f587f3ffcdad2956c5deec225b1d3dd3305a4677be71c9cf4a5edf2550397caeeefb6a7ac93e5b337df2ee7675bfcfc917c1c596a49a1ab8405afff2f51b986aa5a28b989dda03fe2a0140506c8a2e351951c408a0e5dbe19d8a0ce4722ec040547dc71cf953586c0f10fcc03b4f798c27184bd60837efef5fea31825f46e8cd3e5c885724ef62cb95aff70db60b7fe40f7ee3db53fe24b1c8e0758b724eef95d0a9cb915d6dc2bff7c8288b372fa8e516442a51a759f689d8ee3b50872c5b37129226d01d779f3dd5d915ab340d104a26f02a95935c07ab2c35fcb8660e460e42dea6867fcd34b8ebdc03d63d5750a21f121eb8398a7144cdab0e4b91dc7c98daf03f94227dc1b377932541c18a87c8152b0de3cc7d40cf251e332a5f3e0f4466bc5c556e8dbc645d0510566fbeaa3327a3da254b540f97f63be9bcbffbc55f3c2a9afc8800a89ec392697488e58d7c0a7d29df822188a9beb8800d77652fe24220c533e36422904896121cf80ac4f91947f3288b60df0b73270437627f79edc6c4a50a290ab04ec8a1cd4bb653c08507dc3ab933d108a7d35073b6e1fe66bbfbd2da7617e217fcbfd7756bebcb3c931a94932570b9f249d5cbb1d156eaef1dc3d8d406a36389fa543559c76601b4f31bfa3d064c38230b516295b9b132223a8878b42d7bae00e11b6c0d0de84762150ef357d10f3456b0624ead2f3d5466a33e94a19ce0f404ceede01daae1f9cd4704359a359819d3fd9f33fa1a27725840d32e212f8772e10db8e7fb867a0e73dd2087bf3e7673fc1438352bf3800d80741a3442c52246c7e383e3d1e4b475493c1a54caaf8109e954d3d68caebb90ebe3be233e7bd0ac83ab9dfd98a910b8995b2747c909a5152de94ba4499730f822670d8d8460b5b2bea686e152345934d630024a6b6a55a8c92fa5ba32b22ecccd61111fd1328df401dfb9b1bb40ebf7211a28dfcc09d7227a314333fe16e87c0d145c6b86e1131d1bcc2c9c282a10c97e69053812f28048a2e8592312619cad05387d764af40c7f606f9932d21cc5c8a4512986aa893611720a415a1089ec6c94ee994b5994f4333e64acdc6b726afa58d229641c15cfe2ad9aa6b07b0c3d714492f0614a36691c27dbed38c4e4786c975b10d51a3badaea2f5953f9d8fa4b89791c4810256fdcf8c0a5b0b1d04f5e1765b08684e7e1e5be7844742c279a3a6b721ec4abb18e503c13c002f26bbc4ed591c2a48fe38eb62f4ebe639655798ff0cd6b3593c45c254cbfb70fe3b4c57ec41322ccebbb8fe78c3cd6e3afcb83caafad2dcbccf1d479f8487d34498f0d7a29c9d2f4faa41939be6979721858c284db8748291f1f59e79cd2076fc2028a838fd4d136c2c53a95dba376d15bb809228ede99b62ff234846e8d79b1630525424939d0eb735ade9063bb789a10213b88a646377cc172d2362c124bc16316a9da602f112052bae671daa4814c3c557e4b30e85d6631ddd7899949657411440439eab17241f28238e18a8423428268bb2d8ac2f1bba7c7501504f7d74c838fbfbf6c4c32f7a77a45907a63f88f909a829e1af089a0a9861f364977984ff8648c17d280e0c5c529d0498a7d069bbec58923c0a1491306b5e2c4a2e89f27daa4649093dd41d7d037bb2ab62fdfbf67e86982631467ac4d0c0d803b0b5b4528947497915f82b11290cf8c218c0d82a12ed344569aa01de3860fb9ecbdd2f17f5c82a21a3bf021257ff2dd7acfb3f390b41708a64740348fc4ec134cfccddeac260e3057a430f1db0beb66364702924b80fea98e44501d8eb7e4403d76a08d1c572741bf5060794415f28e36586ebeef7f511eda761acd12ff8aeb5ca3b58b4c03b44e9ce372309b2b9f3b8663df86089972bb354c48d7d38a2273c466a91cd8dd2d71fccbf1ac7456664cd6279daa94c646cc9ba17dea5c65590eb5531e1fdf991f2e2f2f415469909610a37f62d9ee59a674f81d8ebe78336daa033101f936dd7a6d954aa0fc521283d08ab601ac6cbefe31b1dacb8bc79967f81103f843f889cc0fa0a3d2051398cb52abdf4bcddd3043e6eaea0f939cc345087bcf77b49a2c19f4ab6db2205c0f3465bc498d1b1f9f2ee07875504fdb9f7c1bf1231dcd49ec059eb71d22f5b76bb0f701a665afb6b703fa79852e3c6d27371d14dabc4eefd3072f113fb0390668382343279faba659117b644b34a51c88a73067a046ab7701648068e810bcdcc91081e9e8680a197c17e065dfb2e5d4e5d616fbebb6b1df02ae4ea251425c5822c46f8036681a227abdf7fa7ed84e6e15257fdcd0830dc5648fe30e2f417aa682b0e494c543afe329b4adae6a53b3fe04ed76f4de0d03c8a4d63cfcc94237749ff882b542700127b33317658b312073746c450d3ea58b5ed6c1d7a767704dd753702f48e19b72915d755ebe09547183476093c2529d5e2bef589c0d0b32dd044650720e56a65795f0a7403068b34ceecf040b774135bedd43b7b2ad11b0c434c471ca600adfda1e2180858f5729e7d44556a6113aa9d208dd212f908862a637ed4aa3fcb43b38dbf481191fe72d8784ad72903fa2157577bf6dfb5271b2471f38190595c7eb3b56b8e023ba32ff699e75896bcf3bd192d1716710396ed2112caa373718b77c3f77f34ea151597b053d467b35db83defd031475220c9ec2a28b6a362886f375eadcdef88b87b3c57b84aecb429d0573f5ffb0f3624bf661704b94ccddf42d78c48a29e69d2d13d2b4e06a67ac2f2bb208636de76a1afcf08939ece469839835025f0e579b3b394a56cd4cdb125f148764a702aac39e4a01a361644723ade9134108d783d0c26031867be39da49fc71d03fbeeb48941e6416fa54ede4a2d334c937ba433ff61ca4cdd3ad87f4414eb8bbd453e3e11afc5f0a35a4b11749feb9912453aeb8c75c73e1972f9073b26cd1486d39e26d76fccd1524a7d0b0563592edfb89653413932cf5721d415a98b4e08b79ea6eaf02af5a01d2a8c3ff6244baff5d2d02ce87f6ffb18a6580caf33eee5da6c901f2ac5e8d0ca29bd454a302707be876a378d621013ead7db4e43a9d667a313a8af2fc289703c66d20b6849b4fe6e2f5fdb75b5d8fd69b8cc883ea68f7b54f0790ea3e947d19fc5927c8cbbcbd634ca05bed4049ef4eb11bf42eecd3b516212e096bc5c3d904db39dd8f4b5a4eb81a44e53bab45976e769a43c7a20b462f2aebcf97532a461e49bf694ada5220e401aa192bfc0fc9bb882e44421d3908b9edb7a4a711728922d6f34ba9c86300fbef97a31b5086731356dd51608f8ac4ad2903d09b7f68b82cdd60efca7e5f46d48d09109faff7d37f192869e76725731e44380e659aa7660f3350569c9db441fe0e242f2cbd3c5676f76944ea842c2999d68492c322f8de03b6be2eaab796d530a10a4b25bc4219545214c632cb3c90dd7e36c02a47a8b418eedda0eb2f69d1baa29b3419bb616b29e8cd39ddca7c7f4d35bb9560f7296105f32a0e70c90a955198bd14543c67fcf7fca5115b01aae2551ac66fc040315e2a5fd13d413134f0f7d3c0bd18439a56712164cca1e6fee01fc8c2cdc86d13502b3fd3737d7297939ba754e877016d2dc5939058fc184b7f58b26af308aed8a925b8a94bb711a388ee4fca28348b00e0557c50cecb8fd4544e6d6b5af6677968170521b98bffe10c997a90dd6fd119e62434550dfce1505e3ff32addeae9562305345045e29180416fd0001463ac12a9dbcad68e044954fe604f80c2718010b6286b57d3dd3799af96ce143c0d2a04b02439b7290852f4f4e7073cc8571fece212695728e28841584a2f60f315dff91376d737fe8a65a854e9cfadb3b1a63aa4025c333b7cb9e24d01f8177fa69403b0aad581496c71f25db147b1787ebdb45a25c0fe2130eef2f3c55fd9eab3a89be571e3d63ff6f03a41dc4a47cb7486ba1224fbeecf0f855f6c2e954b536e40228543831ced74904581f9356e75ded5f90d02d50debd5218243359b6f38f9aa0c38a2152cec4839ea5ba7cc647e3f17ad4e114ae3ec7066e1c751636b31a4eec65f271286fe2f35ed724fde80b37d2181f42c7492240793e97702e934b62faeb1de66370e6110a989b8cffd44ad68cc6653263f9b9c1ed40bd3deaa1d80e8cb20512d3591e1611bf64a4c06abd7defaa4b52c1c810c85664e8ed7fb89feb7343c01cd638c0a5f63d119bba094ec411c00510aafd0e3a82da29ffbef328a18990ff941a97a9177a8bccfe5e60f17690a774f1a152d6d2524bd5edb7ed8e30d6d410ef11c62113d3bd1cd861f101b1d0026ab140202b13c0c21d5cae1537aa9ce88b574fb16e00715ff82ddeaded5afe48b6c285674603bf9ceb6f57ba50c181cd716244605828d74c7a949d227a698aa05fe15af20dacf7ca7811b7df2670cb8b7f91c32340afb13c6d1d6531e0475c2bbf81402cc09548d27596066c98d5cd270f87585e0a534947697ccf2350d5e773a3014159212f641b1722657797ca6189aa32918fe5ef8835f1d3dbd85f4e6bd673f1aa0132c34791f2d2cfb24cc7bc3f7f43a9b8211d66decf494ff79ae8e91c72d10a3ffb79b85a84e90e572ba5acbedb8754246c0531e5b12d492043779cc8d0e0713f4a81c960a8e60354a797401b9ad06d37294c43a5ab62a566d8dc6e859fe0b3e53294dfa5d2645c1db407f9996a33339bee0df57cf4ed5f822e2b3a63ae41fcd7e4e0e2a10ef9a1e4786a6aa63c6bfcf6872b83592105adb71786c056c991c79fe5189b53521f1b3d2850baceabb6cd9f12adfbfaa96183fa43e12ca72b32e943df706dbfdebd57135e75056f0be276d51e190da31143d741836e827aa22097ebb616cb6cf4adf5e035ab8d35e5442aab3330717c0ab5f77902d03cc98d42d2b49e29ec28b39ecb70f9ad99edd8f1f1787df8dcb66fde0357c2af545c56815081b036b8ddf90a266d46528db02e603ddb68c3f76fd034e64524c9bc672390b451d3b1dcc9c967f291f5ad242381aa90ecf15851606412829f6e22d6bfed703fd8cae5a40530831a19cc3991b20b6cdc4cb716244af876033e2741012a3d30e5acb082fde25051476252d6b4bbfeb1c6d9e9a3c8f3a1d277eae36e458c20409e74c7314b02800b3e5df775133e0ab515bffcd96353710ff873586a68f22ef1c84883a0bcfa37da9f3b60925016b56df268067ceb4b1ee2880ccccbe7f67ba017f79b69518a3d6938e48f4f1b91096f65cdc0f74765fd6ae811b2422e976c0039fbe249148f354b79079ef932de9f67d65334a3bc64fbc56f1d615a0e20d7b182f773b8bfa7b3769c2f32474b0560cc803d45ea7189cb72436048e96e691631f70eeb01e87c7d084152f7ec29cc7353ee482a17b1328d90d77688c08ca597a2eb6ad810846cae0b76021d37b510857a562d73698cdbdc4f54fec9050e230da2af550f98e0e00818256856d0983d75da2c79f0bcbeaeadca8483d0b0ad2315f0f3742e9e70f3abbdefed8d6cdd306a678952bc1bbb0dec66f73ceae932922ca0af9187ce31f3a8dba71ba83dd28e38263f4cf8203e52c0277049672b615ec5703852197e8f0f8fa6230f2b734edf0d6e6e72ddc23b9b59da7f7f62695408329279532a9f9aa192356967ef60667020bab4073998a31581b5aa2769e5dc4708329da847757780bde50232030fdf6b1807fb98193b7e3cacaf25a0d7c4799bf9b1d48e98daf16badf314d580c73d32693de4d749a2c748dcd90d7c5c43b854e7b1fe6fd7209d4b47d16eb531f7cbde3a17ebb240b12fb467d0c2d1e873f4481a40c930a7d752cb170079ece62a0859060f81062f0fe6be060cb922e3011addc060a5d3928b2fe65fde80aece1064a6a3cc5da0c9f3a7e98e2d77640894b2a18c2234fe7beeb55b241a9cea1abd795d4d3b1173d4789db2a0eba90a09078b7b29a2327ca87ba607e5e2ce9823319fa10845bbea94dd7c8601d050a2d9c230342c007091c45a7604d4126bd5ba268bc6b96bacf6eebca8e357f96dc9196cfbf1262208bba9cc8c4468f9e020ab07a534bbfb4e19e9bf2d1dd2fa7fa46a807c5d2191998b29eb8c1a2cc49c5d89321fcb84a58d017fbe187f8258450d6743390da2f2e330329d0cd738eb9accb2757aa2ced21ae3be0641306c8d05e7b945717f3a22467da32307db061fdb0f1b7ddf119f9d4c70dee698b9851a5f7afe39ad0517b0f30fb81e286136bd256626fefe97ef6865c712bc0ea64b3c05d5c2671bc09c22329005bbe9b8cdbc6ebab0727d1b5f2be95500317aab579466744c67137c18107dcb8773498048d26c2ad069820b1be6997bd7d0fd2a9efc58321278a3815ee1290bc714c460163c534f2a67a207b72771b9f75b5e09772cedcc841485c3f761c34f787a0fd0cfe3a349d19616f1c8f2f3408b79eab7402edb8642e4fab97e81b84f46a8a7afa6dda8309ff50c780133c4bfe41391118ac47da47f6c64c65310f466d93d50624cc5ae498e137766c29992e4f540831934b104f34317917ec361d725b9c2122fd72318be3b95e1c228bbc5ced08aa9178d4c09f1c3fdce06b3d0ad6aa3ed19456e163d727406b9e1e12359a8e1ca458affba8795cb626ee243e056c563d07018c4d5576310ab32e0ad890618d5bd0b4c340df49a3324601e0c61669fa62830383f3ead58b2360986baa7084f38bf54e4ec380d9ca67fd9563b61c42ef088fad28f92bcbfae2603a3bbe0e3b3ebea40bdf7f0d1b00a997ff5e1b15e487a2745b2ad061b89772c2caca6942f8ea111d61349cf64adabd842845c7037ac83db56f677018835f0adec97a2e23d615a9affb8c114ae30474af028d862d169ab4add11ac133a9f0f86ed72ebe6784249b62cec4dd70d66d842a7004271fc4a532858f1392bc3f447beea906c3ad8a2600c296e80c72501f1ce9d0a3652f1b51b6707c40d8e4b03a907d3b61b8d77a6b28b0361d25f5fa852bedd4308f0d1bd4617a908f95dd7d4d61f02b6299c2efa5daedf8b276ffc60fe6ed7c74f92aa265ddbc2997e483ab7d72020b7bd63088c46ca4387deacc6af4becd1d514928a4e52bbd565a9ff3ea24c8cc32653327868e836d1c6d70d9f2dfb85f8ad71b2937c47ef2e32b728d43d5fde76a027353ff5715db2825010ee75c02f650b0d31dd46ebca018f446acbdf62a40fd7f462ce908ea79b56992be5ee319cfe0c5120dceebbf0577e73fb6f9522ac9291b1aa478ace1f77bd3c27ba8dbae11ac01ef1e311c049104ff89d1d03a814aea07d10571bb6c3a1a03d199c166c9d89c8a6f0d8817b59b5f96711d9a76253cdcd4780752bdfad35ab53f139213516a0f590206ab60816a58c3d37c7289c38e5451785161a99fdf0a8ed655253225d2a472a4194c4efb32d4241593cd470a69d14bce7e77d62b3516377b7714d7cc72c528dc2147d770e033a46e87c8dcc59d1f80ac013a907d88abcb8ead6dcb38b0a316d11b55f328f6419cdbce83f354d7c6c21ced11005f2e797af3ab2b493623098b0408c68bd7ee4105b99ac2582ac6934481c35276e0e97f4282d52de0a9c551d6a11cbd2c6ac7b94d2703b8970beb49631fdf0b185ac097894c055a4f1e25b6bd0c09d60a6817d44e48213af231b0adc86ac26551a5ee1cb95a63ff0f6266d4ac6d08247cafa25b0bf66ac26718882c81826140e45d0ed00a923d0392d7b94d5bfc1d3363dc3370c556c4deb3aaf1f6062dfdce1a64ffea0c70a7f3c4b596fa5d8c0d199eadb28c99f5d52c29d62d8da00274a0a7f995c4986424d1ca434c105a881c53e3433fd5850470241659b7cc079cc4c24ad05a0ca557bee7144948bdb43b178afe29a44f2990bfa8c1bc29781d110d55e6819c13cf141a870b45875abb402dff7d7b76a82e59083e99f95151f279e23ebb951eed4566d885e7266529eefd295b7597c2687529f861e6ccf0cd557dfb67ead6417b7932e5986058bffcc30d082b82b37a193b1a4707029f3537de2804a812d6163e56c3aaf22ea3a785c86b1dab7793e05c29ea6b322854a1134295ac17658626b964365cd15b4fd9ff16c027a54afc15889e3007a0348f9e7bbfe505d79a9216d794a873b4c83d5a830e5414860baa803026759fa3bcc5e37172863e6b7e4d70e344871443d0c5cab8d4b601a98111433657f2770c31c112fd57cc42c9245cb151cb1aeadd23a026e436d723a267d29760a4b339780a32c41c7f5a6510650cf324e6d688b758f2f9cebf7c19347c3881ed00a950e0a3cea706b404f45e9b1d8f608229d18ec6f55b4d2821179d341f4e781ac9094a28b5cf4c1e0c6222f49a788db9c774621ff49daa3c4200b0260ec89dc1889f9fec750ed80d6c3c2be8130ebae08ea208cb1dbcdaf0760b4412c2b2183a26cbdc043c9ac34f6b8d15c9cecb94490b951a8559b914da3e0f5a32d3350d6e7b81e3f45c78f1837ea1fcfe64e14768706b01fa1b6c4f61bb10695d7a599f27c0989a820218fe22c5676a8bd30db5390abcab432ef521b6341e7d89351ec3c66305f2c0ac6b1cf4b0ce11dc6a847e6583a96a934d20e464a6290be30d8456c1aebac52cc6a5d0d7d260f9df4bb30bd088ae6362b3eb6cd9b6049e506f33dd3ce4078afa704c361953bdf6c573203529f2a6a0070f560809722476d300779e00e14bbac5a882768ec7c26acc505ce1daa807301b883b04dba7283c5c9064142e834e6713920dd9d2d161d0b363068a731e11f14c5f792b597a0fe87b75e67c58d24d76cf08dd939e1272b756e5eae7b3c620e663f10f2935fc1ce194e5b7ef43a203a8bdaab9ca888ce83ca654d5e59270b9d210d7f64a198b0e12c66d667f7f71c4cb1314ec50ecc7d129debaa2327b182257e5648a7a27f8205433d8457aa829ff0eced68d08accbadf6f975fd31159fc4c097ae81ecb1f60f7fe9c234d7074e92b6333b0b819cecfaffd18dd2977434319c373a0e4d34ff1115c1b80a81e1a261e9369ccec0c082defa52fe8174a668d8aef64c2740e9b107f6a805616f79f4fec5900fc61792c4a71f2212f6d59f7475329eff567da947133d4f8c0c07b34ebd0db3f01aff1702e6b2c1eb8354f6c9c6dff3a4f75259da0b81d5a2a0b1047d2365ccfeee0ed9c96cf199cf703ff80c9afa6d6224d30227199193c646df631334511cb4003531d4f0eeaf19cfaa2adb9915db7011deabde265b6713610f82d9ced0fa5684bed0807b31c1b27707bb1c3c6acf4a5407ffac064015253debdefd6e0054d89a405308fbec4f9173807df2659340190744f4b5232950c2cd17188e521b81db6df3b6b1e39caf938913c13f93df8ade49a5ff4a07286c295dc83dd453eada3bfa1750fa12bef03e44f24e171a622b7212107ef4091ad7b9e66eec303955e11c51085ee170186876d7c2acbbec43835c6c97b16505f41043d8415ce73b3bf2de37811737de54a20eb9f050bdfcaf0d624f5c3dba2aae5d982d5e01e2699dbfab33228921a2876caa668275693665268fe14f1d9f127503ad77cca35b54cc3f1475ffbfb29900c23cc3b2c1512258f9bcc823263e1bd27cdc52dfd62dad4925033970d39f0d65114b0118963cf015a8d58973e76d0024ba4de3892b22978e5e439cd528bbac8ba666034052af00f9402844683839100b8c0783efaad35198945c3d98726886c596d91918427e81a222fe4426184a2070889b115fb5bf0c4a757cf56f755db917ae10525f6604cd85613dfe647dcb0507c888282ceac7b445f109811bf5f3d509d5d5455c9a9e9df1693a87ca3976074bfc8a21a1ee53baa63cca744869b825c4f57656d8a4102448ffa04b994faf0d8340fb40a86f67d1ae0898ab2ab863a763cc8ab45a58c96e02f5d2013107632608637dc8dc6a019d6af75a1ccd7b0e350d0c8e5a161ddc1c08e7c9363c41b7126a7ca47fb8a4bd3e38f0b529785cdd02109450bdd55bbcb3ed03c755337b0e1f0bf6717ca7fce898a35280edf6f2c6b47bde31b0a86ce835a0dc9c10f891ba0c7fa8d1b2c54e27d37c5adfb06ffd4c78ff6373fd724c7ce69119365cbfbb0a241de27fd2ee94b23135b9df33621272e75d9cf9560635bb1eab4c6f60f75a84009783951dca4cc3f935b6827c4ecff13f473b7c11b0f96a5be7718b24b2f0f62a5f7ed6c5161ba762869f0c8b6cc009b07cb8452ae6104fdb799767434f9ec52bf3b87f7aa35a13cfe46e2318be378b3f9bddf4ea9223442cfe05c4b18bac0e271f21fc429b31ebce90f705c31f6c19d9781feff3ecffdfc261518d454f12691da279357dfab133eb08889699f75174df8dfbbc381b48de70c80a440cb666dc0a7030f8c8ecab1c1c97d5c7f2645d60891a391208593f8d7e5aa04bdcc9d50209356eaa0695cb0e5a49b0dfd5440d958309583540c02d13b3fcb4597176c3d50c9c16dece31449cd2574a45d99fc4687374c94602102f5b46246ff60e6b37372a6809108be44bf7b89c493bf154df1373f8d9086392977ca13e2cb89de5adc219f76c71cf919bc6bda4d0fd6d0d07d4ec925b2ce2ec19e9c3c0241608419d861c096b79285ba465534064948e058a3bee97438896dbb2f4cb40c7cb0ccf41af0996f8809b176892e0c5da063c36a6930a3b17308a2c4e8dd0aaa51e72b29bf0215ca78f71143567fbf9374d7e9793e93d6a3458998b8bff08d5709f1e58f63b0736690dbb85283c9d46ed82c2d90a3bb69c84617c7c88e4c5cc9d79d66dc710262151641d45ba24f9a918510327fc2d31b426b92795ed9f090ef559cf30f1201f8d0d0f78d7898a04afc5fe83eeccc1de82945db199064057cc868e6900ce7c05617799dbef66552ad082b83f9eba55a7ae4fda9acc94e5c8ac7cf11ae65053062921b2287074e630a6f20c1fb6acb692eff24c1c9fe9a1f842b571fff806e625f4a286a5725188640abced4f491d781c8597161fd5a4bb98d90ef9826f599325be04d44fc983ffe469a70f28b4bdcd7a044abd612a4bb4ce22cd3590767d2d0f5a272080032fc298aad59b56a8c04b935c4f5f61f2c882ff010843359cc5722702cf8db27c7ebe19821f6a1fa2b45378ccd62678f2bbdba7fca7e666e3994a17792e281bab19d98bb9e4ff096b79c3af935b151cf0a796a74874ce1dac0bc11dcffd953af6e5d33e9a44f4b6b5cf11c1adb4658ce8cbf61a3469ab8a69de5e08c0469b21420d91cff9bbf8d4b0d3819332ef0e3271d5d58b93a79598e119ba1f61b8b893ef57a83c6bbd4bbf2dd11dd7688b7eaefac01707fa0fb1c14976711a468f53c7b7599ac1b6c1b36e96d2f32680e250b2f9d1f7bf7193d4442423be150dbb9b7e88f9773311590cc888b7c93146e9c869aff64029aa405e9a02de9416b1de1fd80ac6fdac97671b88ed413842bdbbe4df0a6d58597d9f1bb0372bfe1ead477ea0920fd1ea8163d17bf85a16a33328d534c93da80b340b1434000e58031dc61005d0fd180e6a14487f716cb16fe16b88a5a18fe297be374f46b99f2a08f971573d23bd50fd55207f764d082c45977064c78ca0c98f5b03aa716306bc0c1b172fcb4d88b6c2f082316863d54798c916edf1e8f676a2d3d13d2400989f093989b19720188136a6402888ca5b6aae2ebe548f2bd13b433efeccc791810119897052bbc6759c9b9c133bafda465b75a2007d875aa59572c04dfad21609c5131bd9f0ed42884c123fac7a35e477de7fcdaf2c6c15e80c7f2de1b7ad5b06fb117a1a56edab8637fd959382fe27bd2cb7b7e0ba2bf58183aac386238fd609472642e995b1dedc27f03ddd11ed8513d43058ebccbad0530dc9fb591ea271bf0b1b19ca21b1722962c2ccb3f36299db873c652e6fce58e5bc7b0ad2e88f7adec42cba039aedfc64da34a85cd47c3af5d27c4edea22a973d5d6c41ee1ce66016ea215a5276ad1b3f1d6e3a016692962a9bf07b77ebfea88e47516d890c8d21d5b21ae20bf3e1422de4c93d5b061bd8478db46472da3e5e09ed7a9fb454eee320b4235392465ee8d32e4e2d719bac58417d831b9c3dce74aec833b48574770779380a3fc55e22da21db976bdf202006299dc9dfe22aa5de25723b1ea5adfb04b8fd2bd57faf1e947f33379978913e7ec27f4e025cf013a7f20a14d910b4f4030a0b0670a5e458d75f5de7b8470964d20e02a18a292d3583a6cb04ca0b88342b61d6030e48bbc07de132df81dc65ad5b3ed95c2adfab886dc91d151a9d4edd1a1d87299830d9b3137644b4cbfbdfcf79ca04845f9f205ef074d52598a0334d44a16d0596c56820ceb0c74c6ed64b4e038380ae235f3a1e03092f703b593e90a111ff06812849a90f4292587df08935efdb3f016c7eed02d8fca01111ebf8cbad23532aff6c0823528961c992e7a60be74c514cbfeb62c3dc915df54e6ea945195ecb1a38ce6a1d6d855aa8e937ad88cc93c2350903fa0ffcb7741d5d69ae61ec19df12b28a847765063a379bf3063f036b000bf4857efac2e94b7e3d4f497d2c30192f31eeef0059b5f1a741b799eb2cad48805df63a89b47c4d7bc3c197848fcf952ce299c4455782b74e8548982cdc5e5419342df6a271bf286c4040ba56b22e7b0999cfe1bc2ab84d82a5a7561de669b30e15153a7526d19fd36705776faeb40f0aabc1ca6017427fa83fc862dc0b6bdcfa4e75acad8a5bfb070e3a38893461fe0bc647b4d478307d5eb5949e57b9895ff33d7ba47210ba78711a0a77669d04cf2a8d0c0af5eb4a1462aff2fb866e200cd1d530bd23c40ca45a8833242363eac30836a34e5a7d62438533cc839950f9f57d822526fa23da72ef4784349ae6d0ca0ef6b88b75895ca69b8163e77c95afd817bfb9f8f215f2d87954f546a007decd0dc668c647572a30343d9d12fa69845d50cd2fc6234942b22b9b6d8b6bd814f72ea1dcaea80d09e992d120228e1f67a8a4b70c6674aacd146f0d881ab8130abe104df4ed61f189f4889b10dc96d83e9bc6e872b54f626379d5e14be79625913b1e579c015c34a47d00a1043436c23bf6b4cdb23582f920d2b9b5546d10cc0a8c2d1590f7e5203d7ad40ca70782c9b35aecfeed035626e5de084fb09f64b20592", - "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e97475078032a555ccad424037773cae7569cd26f46467d9bd96c32334af741c911096761830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000020efbc1b4676f6018902362ead77e27d0000000000000000000000000000000060ec24b679943ff559895776f6f11c442197e95de61c6c2844727c848aa819fe89bff180954ba01135f7e3d285aa3d8e2aadc7250d71617ec5c2f852c2e49ed3180111ef6b130ece8b3504a7e2c3d0372c30f9e94afea47a8631132310e6d72615ccd3227fba92d2ae829172e7bf1c2d24fa1f864f85046e7f1b15accc6c376dfd7c6960cfa3b7fa0662cd10007c0ef7089943510caae41bc6adc5e284e6194b797027236225a98865400f364685fd6609bd127e0b1c620665f422cca7d6dbde8d84465e9268825c7e12feeaf35902160bf36fd6ac0eb33261fdbfd25f0c3cf65632e1be186bed5531a223cf9729cf8d2ea020aec653a1abf28081da5f3283ab32f9ebac85d38f7fb2c6b251fe701d34" + "proof_hex": "0x00000000000000000000000000000000000000000000000ddc537f44b53fe45e000000000000000000000000000000000000000000000005d2c364993f27cb360000000000000000000000000000000000000000000000036a97352d8068108600000000000000000000000000000000000000000000000000001db8386de0ea00000000000000000000000000000000000000000000000002bc93d2aa2c401600000000000000000000000000000000000000000000000c0d14f10c5fbb573a00000000000000000000000000000000000000000000000c707bc7be0aaa260c0000000000000000000000000000000000000000000000000002b67cbdce2a1e00000000000000000000000000000000000000000000000cd16482194b961ccf00000000000000000000000000000000000000000000000b86ad9a1e323001d700000000000000000000000000000000000000000000000de1eb22a6bb20778c00000000000000000000000000000000000000000000000000027eea9bef8094000000000000000000000000000000000000000000000002fc183d9b404e5b4f00000000000000000000000000000000000000000000000f8385a2178429603000000000000000000000000000000000000000000000000cb22ed841d6eb43670000000000000000000000000000000000000000000000000000cb1fcdefcffb1d74bb7961ace3f2f70c4e830de967288c03b173bd5158d3e5cd1d7fc55e865a03cd83a7ec11ca7d54aed446132af02f106c160b605361787022be5c8a7d0fd01c35e411edcdb143c708c052dfdedd4e54fc380814b419aa5341068557f1e98511bc88a7061ee755e52288824c391786d6adf9a09090ebdc23941b5572d247e416da2f3733c9efb99ba96eb277e14f3858272621b15735c4cf986eb6f9922ab32bb3beb4a44733ebabe5d94219e318b866bc934894e61df208758067275f6a7e1683d765e17405f0e76a58240bd4c830d911f26e464c227e57d2e92c9a1e85210ce7ac20d92d0db6dc8f9b9f63e554bb78536b2301522f2901d1fca0b0adee9402b670ab72f4bd26fa49e85c45951a984d4bee6c4bf2070544238ed41f1adaeb1ec052c80e75de7cb223007b5f194ed2f28d04cede81a64ab07783f001b76d942d16c4d740f877adb2dea5cb85ea18004f92f4f00e54481fefcd1115186ab43a0acfa1ca0a228aeaacf7ce2429f7a4e9581a243c4029a443ef72a3a321e1682d14a7f2f2757df78eff6768d1f653b2a2994b47864fc4f80e532b46354245b7a02a6e00ba0057129b07cf30279612ab0a64e41eaee79db02dd32ee1eae16f626201d6f39a4950369f70b76175c6dce4ba02eb128ebd52cb75682acc63f21b52792eb92c25245ec066ae29f6a9b69e7980e89a66b0e8ed0b8461e1a3cff3a41629169bb7a7faf7b158af57ac2a8814736fa48f50908a1cffc783e3dcea358a8dab05d4e8b1394a69227bf3fe6686bbd41b65ea8865f9616b853e55d01c263cf4601d3b429b3fbdbf6c3ccbc8bc7613a55080d4952205bea66a6487d50bdfdea2da1966d442fbb7b6d5de94a7f1f66b8f2be77d33671744de4db5ae847949c06e0509fb78c99816ad278049888bb597f7b08870142bb7cac68ad7bd9866571943eb26f4a3efeb2de8d56203502ad26bf6d582fafe6e893716505fca3da244cc36a329057929704d19799de43956ba1863b26da9a90cacdd883e5afe90272482e3831652a8b3992509c56f1c37be3f7da0d5ce3f5dff03f2f28f9f8971e78c0e257129790bad219e62911680c2b8262c491a19c65a69e2801de14920a8e49539f7782820f32def40017e1af00ec6d325e4c82b26d4dc738596294c15ecec975ad3b00ba9388f8956cfbafba1f6c76a2a9367c27738125e6f97cad3118711c432342e01a4efa9335931898b9d34cab011ec52bc1f426517facd578e65d3937685f8192b4abb55aaa96dfa5fc246f5bbe5bc9777f78e01e8c44a8fdb90646fd71d0d641738a84b0f2d2d822d017cbd5c5d9d7363eeb71b6098d280119b1ecf9b90f4610307dc478a39d30eb8998fad0001bd00ff2bed07374e5dd387830c146323519e08f35ab390d4282810ac470f17b1ca6326ea2421d6119c6742dcaacd76c30286282dba21e7d7389bde163c696622d88838635b4a546d17f1600c18b748c9f72c2553375c44b53f71a52bca0c87762df6552408a5f4500965614a5b03d4fede551658351460482c296ccb4154aab50e4ccca5cdc55fa75423c3d84c64f70518b208aa01ded1676e0474d468ad377ca4d2b89ab3cb3d3963de0a6b13b1debce552033885824e78ea0027af972aa760ccfc5e7fe7755a65a6c6e4d532a33aa47c39099e1828b109c1ac9114251607526ecbbaafb83e8fe2efb0c5388ce62ea3711b2b28c132f05e1a371ceaa48cbb778b4db0530d863efe66dc5a00840ee3bdc0f71cda948b42a5a7059b54dfbec5be13a982ecd3d08e07db4e9b740afe7b136f6129ebc34c7ec5021908b52d65363cd12854f10066c1a0e09a9733d4a304ed41801ee2ccf5b72db94d42ead97f746f5b92eabd9115d8d3c47c7198f0c59f61c82c2e6cd40132c27b3a7014a3d0404857ace260b2cef00871951e08d2fe4af80cdc20b67b6dec13d790f840cd45bc09fb36f7e9edb1576aac1fed6309d69d9b4bdc23a372afc89b2b8c7f8f0b4d5a8a9964d817ec367098dc3baf84a14233a8b24c160d1e3cc9588f8c29ee86bf38c8467751c54dc206ecd20dbc041b9edb0efcf721eb824ff79a781cfc29baa77eadc827e1faa60d2ca595aaaf41b05f47403df5054d2e48bd550fc31e258c9aea79d587deff0e66ab7e24df5ef60a675dc0d5d61b9ff4f004a2ccd4320ba46ab42593fa9ff56741d268418ad57e8cc69060e112173229ebb2deb9626294c2c341417b9274d7624bf0f7bfad9d66b10d28b8a03116f1913cce922e6dba33eb29a98210fb69f529a830d6040c5f0117608ca3ceeb2301f5fa825d225c3bbc88b05c33ea0e41eb014c66441d5a9947fc4680d35e421dee79f2143d0497c9179af370b754e0224e003d05fecab656f64a823fefe44313b30bf0007ad379d1ca866ab25f493e8e18900fb389ac5415f67156120bd77c1d83e3909193ba0d2b16ad19811ceb4ad3d742b50246378e6fb6e45468e9008a1775f8bde9dc6a8231c8f474efdd6f11f695e86315777cc234f2c04bf87252c12fc8850ee7143a98fb139e12b5cddcb9215993cd57f93020e31082160c1540fa0ee8fa70c9c36ae886a7e4a52840ac356a9c28b4a0cc432f0a57d049d9c60ea52f9afcaa6d4f8d79c21ae5b04777811f9960f0fffd52580320d0fa196009634e0b20b84cf5dda38ead8c9dfd830c5aeba4fc6a0d4ef749993b144954888993560f5144b9c881f632066f6c63c95f4066fafb419bd9601c028ca2546fd6c3efda0b99e191422cdca5d0e71bbad1c8f5e358b973d75042b1dbe0d437393ada02fe22b41dfbcfc9155f55bcc64ecf82d0a67d810009043901d592b0a810c89a910b1ea0e956bf5eed9782a9ee48cc3a97b989f1ccfd38e3a2c24cbed0109ef89d151c96ad5afa7c6d2e7219744a1338debcb9f41485d602f9c2cf30efb8f22adf390291fcf0d2a9ec8464d8180854056c10dadbb17a051f9c2fa1389962ae877246190150bc8fdf39bb570e596c3ef14377483c735c8325a32b2709ca1e939c9d3304855cb7c5d17fad910f9842c598b174fa9c275096076e45af017ffe4d0f9b41129d3cfe4ec19dc4f72a61ce91e290353e9d411dab365e3cfc43583fb9c8be7c2e0f7417e6953d9c8116c57bbde9964dc413f454ba813aa314c4c9dce76b00c9136add93dccb1318d338c95faf456f6ce48ddfea69c406741a3e59dbc207cc9a190806c021ece63a687d90e4ff7fc6172c53f1b23827409310b5a44f62fa60292055a94c5a8d3471dcb94e967000cb2decbcfa85aa6bfbe4743e08a1ee230ace1efac82370ba2f09fa77016274bbdd487475e100862dc8fbb80382bc69de285c0bd0047ba0aa8ec022bc23168d5afa5b1d3a1ad7bd4b9c2ebd431d3bbce150a70f29dd3aada8fba9f7cd5d5db945f024a9dea0dc51a27752f0be20b4483ed1f5134d34dfc8dda93da80b5cb0135d3e4f1b3acd5895afe9f691dfe5a41109caf52391b88832ea4c3d2b8627b03beda0692826fb896c237e415c9058a78be861841bb561ac86c2c8800167ddcec4067160125bbb52a30f0a1c7a8cbc2cf4e1dcc107d74dcf175389be3287894d9a8ca93a7f13f4b482f4d96b5a755388c175b67f077bf625750b2cc8492e3829624f6acaf7206c352edcb0860416f30c5900359518496a52f58dcffa59213f02a3968e939b821acf25c1d54ac8e27b5037799a971a45440c2bf0c8294f291765bb3f195dafc6cd825641f439c909de60f22df7d90570f8581fd144d8e336e732cf807c575696fc5974beb6d327d0f4e50fdece5c20104dda0e51dcec48d30f4ffe4765a5fa9f4a2bdcc2ba09ad027c2ebe4188cf02c493f0beec1b3aec852dff73de5ba65fd4704359f4e7954158bf624d937a0b09c929bc6fd95c537456c59599363f86fdfdf5b7d2b58ef05fb1b62c4f93945e1e9b7f217eab940cbd9d3dc027a2614a0e0ac55d6a5f4fc517c77a610772a6d5067e09832583efad6905bdbec52a599aa83ba05aa495f7cff70fe7fef1e8f9e322b71ffe815a45f39d91a5606a09f8725c76214aab8c32bc3a2ca3d1c4f6f342210f1e70ba357d8e93e48c3fb547aba790c9548057837506e9d4023e026050ca154cc3aaf0c7d60316c7f56a6ba004273b5c1a08881c83cd592b770e063f5db6194b4543e50e6b2d4fd1828703ebcd61f0e0e8238c7c2907f640ad2245e6f8df296178c5b6c882031dd6f0b4769251c1cc7243b4137447d868802cef801b2b7c214299d10918e2da7ef9a09e2bd9e11432798d3054d50e494c0da81d33d89a2607f681bd3f241f731f09477538456c7b3a0157ce568d7d4c828a4c24dce4e2b10cea115a9802c9cfbc41c26092502ace91f9937f175d77e3f165ee57b2ae6148293043f2a0c21b1110c61e1d48ebd32277db4f3f8fa1a44148cdcf7ae17d901310ba46c7249cb240b5c8e89ccbc193ea405bd845baf1616b538abd45acd32e250b0eb91ba32c5f67e81b79cc12e4b202a3a514ddf1c33f55d76a78dd2a6f431216cd9459be0440312a230538e2c08e9eb699323c3ea330fca18c022801cc6bcf1be0daec57bbbd18d2b11084198ea0cbe1d5b1f16f4c9c2f912d01e6f8b386610a28f67f554cd1490c99771a65f50623d2fd504f9c4ed37c92011ff0b00fd73f2e96733327ef59d02451b02b31243d6de810ab7d4bbe38cc5c4132712f9e649b292aeed91c210ad7f26e8b460f9a2cbd737062f3479f6790b0d85e6b128901da2c988129a0ed53916b48753bbce885de1a2f74afef8cf38b2dd4d8aad9fd5a7e13f32b602a56d2fe14c56a40cdb217be8516b43d718ae6218a02c8489fbe23562cfec06264e235fb768619a1c68daee1f5343fa2fbc9dd496b59aca837eafeca1c526e5ad567e4a3c658bd6de68af23a0a436df9f7f3f29f0ff683cc517a04580fa8bd2d49661956354885fb019f0cd95b7b7db7d8de723a7cf201babafb415f06da7a7d803e8584a58a41c24ae54f7ea86798affd2ec39e5971f63887d3bc631b8ebd9aebb270eebe21f676d194077a0492a039335bbc974e1a7e8e5e3b187b005cfce57954aa83f4f1373b857dd0e9108f15b8a3319cef2baf99a947447a6e0970c4556b9cf6a5f7c9b1c4a16e1327cfbb978db10ea891a2ef12d50a37cb040ff5378b30441161af327ce465ca56b4093e3eda29ec439c89dc32425bd33a6b0259d8d1d4319bab6eda9a614f0f78eee496b69ab9fdd9aedc3391378993c459027d87ba74779c68221b5b1e24712803e9f034793753d49d661fcc34af88b67a2eb483c76fa9350617c43cb676482acc4e4516cabb406264d4e41fed5f4d800b197e44dd76820dc1d207e1eadb5fbf61ac15b6dd1c7f34dba0210c84606971fb2094e779db97d0fe0250b61846c330025c5c951e7ea310f0482708886d6288c12527abb45cb435c1579afd6dea2799b69e5298f3e6325f2926583697b5e2b5e21515e902fd878d2706b6377e4a86dd3e75e9f1c6d5358b69898d7579e05df9ea09955053c4b5d8f47e09260b20094fc317222707a15fcad33d54ebb0a286f20522163de47ce56a75b960afcf3ad78fe489bce9499c2aaba40725da1d0d3753eb23e9215f54dc85bdd4633ee04cc3feac16e1961804fbc82b69144f0d02cdc09b08635aec4f87f85d0f8c2c235c257389f68de57c17570ea93bbdeb89aa489c7e2d06f6ddde1845c36d494c75adaeaf4e7f71ab4c3ddbff29c3a02b1d2df0bdff2fd17b65e8f3ef08cc0bfc3298b30a9ecbfa8748e5dfa71a6c479b7c5a2c68e60b8b5d197e76d358697acd62ca4d0f0b4ab5c7a1a6947508e0f1c5f9af1a4d2402ac2c8f342cff48a42c6cba919a995820f017a9215ad63a7cf6b85aea4bb44a2ddd40c367f7b0d4db0755f1f0513e8524e2fbcc4ea1cc65eb312ac3a0ff85f4287e803ad96215b8d15abb6a304a8c0f13bfb0d673d9b9a188e8bd6b56de3ee426c3ccbd67dd0b3996a9719948469dad9f074a13094f68e012cb4f6dcc18a9d92c504aaa12f96b40e0dcc1822332351be7c39d99a32b099abd4bef860b50e58a03bdb1f0fdd97afd4b44426fbbd310c7162282cb6cc5681a5d34c8792e7671840661eb520c57338c07b2b400bff64f9098a66d2aa940eeb9dd8d080e274620b022e6a97b9dea474a50239371cc56bed9a42501ee62a633aa6808daf395da3c8816947be7efd0e3c76672fd721740f8984d4c0529770f90560ba8a139c4f7ac6523acf595db0a22ad14e9552813447cb062fc03588da0bec34a24d282a7de1889063cd32f9dba2749bed618a058e3be4c300735d339e3f39afb597c470c287c3620c845653d8bda6ff9403c4e297a697a09dec0e9eac2cc5f590f152ddf12b0bd1f0425e8814a418457d8ced41d0d54e10057d8fe626ed42f6a69a7981809fb310a321c501eaf6f32dd062bdc21a63ad2b9a509a0d7d7362972449fba64911a3f0c850b147619d6f54f6437612fa2c18d1bec1016c640fe49367c05a1096894e4033f004ac462a4c4fc2aacf97f4303834e99878ccd540822891a8cf1baccc0042989e91fa78091f21264783ee35463fda706bd6bf6c3741cc7191892050c36c611395fae2e9bcdbb8d8d4a820a1391d5b4395cffb38f1a23d3ad3389c4e13e601842eec0dc4f455637e9912489865c89a373fa4d6e0cdd95b9a36043032523cf197a2f7a626193b18e466a3f5333ab0a576b7a6c572c8b67352f13d570809d6d0ae6457f3be1367321458a0e4cc1453d02460bc831f9c268488ad05f1433ccd0271ecde0885fc671c03a6d62646ea177e0bd6d7a3c579ad57ae0cafa74ccca141c15b844d1c1ab9deffc01df3f7fa8396ac7fa4c7576f689f22949b2915bd6c9161b73eeade29e2603da9d7d86e6cb95baa6c8bd0381b284378b2cf964fcbeee2d05ae518781466b3302f7561b144d6ac87de4bc42056911ca7f594555e4a5bd2c228205de015357c2903f7d9172e9789359250088e4797c458c6f439f57ec881fbbca82dc4ec32547233fd8b9d0f464e8c9cd656e151e6e7f573cce989e8335259537582f5e8096dc5b6c1a2d5f1941b9d01761ab23811b7e86df9d9a9ff7de2304576421d982b42b61ddc6148e03a73deee2e734616c495af2eb08c76cce640795dff87432b303fd8552a8e0bfc097667dde8f1434a2a4458e7e3e63b2d2e31fcc077096bd6aa45ffe9c70ae160cb4d5e2d28854bd889ba3155ce0ef9d087002539dc1ca5eaa4c94ee4f6b8f8199a1ddc619a061c261bda03b79c8c1ff158124ddd94fbe30c2ba65f28ecd47d77724340ec1fb1143c150a014e2241b78c8801364f1b969518cd9d8a46e0a4b2526680351086bfe08d3a0611f5402fc8ff7a2253c9dc9121f43c741d9059fdd5a5bfc68b8367576fcefeb2103d8f2057614a42e38726adf28f52dbc15715e283dd4d3e315ebea3d87cb5681e648783c45631b17489def600de32fe48770d6f8b47af009996c343952dc446322afe142c7a0ae2fa2ae3dd68945a1e5b1cb742a36aad5bf6ece555e28863ffcbbddd985b201132a1a3a08ac58724b1cd5330dd46b18833b3349816a5dd935d98cf89215f41bd51294aaff14004a783e89eec6acd648afb645793dfcd5e4ab7a6406a7b06c85cf00533eaf77c6ddbff3d25aa4839cc5d6aa0f14a5925c0a32d9a445768524164e2da427ccf8e54b7a9bf52299e01b91c868cf5b4044b45ef5ca65da8d3ee799fa20549181f77be66bc9817005e53bd1e531e6dee9dd0f20ca8c3fca42ae8908872fb3427f975bf8bdbbc304d78748317f4b67bd3dc7de4fe9b1b157edff159e561ebb0f00f5b33cb3909a8a497869f51887c3db6a9b772f81fe2884ede9caa523046986bd5d116527e1ea70b1f80828a499e31debc9b76844336025ad1b21b3d2015c6b1a54f0545b2cb529e8afeda16aa48ce0d0ece560f74ef2f581c66a081600066eba0b1bf195e16462af77610d6a151195830bd0d055bca6d1849483129a18ea91d797dfc1e0f6d07a3436d18c0c5f334a5c4aadf2e5056d19d0159c715e2cd650319d32667e302d8a9fef3af6a9545e0849f21a07796e59057adc42627a0a5f8c5ea318a090a49792860132f0fd5d4919ee50f200416a5a2af10acc58971f811402abf9047c9fccbabc6f83492467fb15148eefbf12e03cf4d5dc73c8b11cdc329719b4f8c52eaaf5815a0e933a84288d3b82bd6d386fbc4aeb2bc54ae70d67171c67f54db3f36baefa981e124737f1ba0695c790535ad233b152fa5892175ef3e3f23f536a8d0a6652c20e2e82576798cdbfcd649236dfc11538f345b405005ac1a5ccf6b49759d8798973e0df4e77e0c89fd098e8419abfe2f5896067246380154e1c9194733a0642df08c2e1298030c2cda913afccfd1f25e5bc92921e6e5271b9d827c85fe64ec46d1d6b1b7c7652c3fb8aea513e179568c8b9fae1064208d306916576137f0074229737030a22f693a6e1e27ba3a2799240cf04f21a4906b79794c495cc5a0c3172f64240935363b2a03b8a498403055b57fdcbe60e976d7a65534759f378a8367c72ca4e53cba16fb2fd4c269be99f9c0b95766e199b0cd0ea8195a74b08a789ef979eaff7f5b1f2d2304b25fe75959e4535e2842c510fd683ce6a57e9f88241a6f5e1c83b344d1e48e8eef6207f4c695a7160e42d9da2b737bfac00c86673a90cf1af4207b91965c1452df1cdbce7fcec877fe50ffb6145770c0185228a56decdab132677f095f41faa588913a7dbf6d99b9ebc195574350eb6ffec25f1454cb04f10bac2497afa7c07b1fe972a0d9a5130df5200d03cd4e2d8dd918eba724e04844ae0ef024e4d0816c80701c32d846e69b3d1099ab8d3f7a5bdc46fd4ebcfcf083126cd4f29058da7f6527510a56b2728c5a305a5890ea3956c25e5e70be606eed43f110c13e4e5c62a295060104eed434c290ccd370073feb94b0cb270833420422239240910eb8bf68d9e64695bae4ae47b0faa67b705e5dc498a3de1fea3e10d1f180ff73a45e47adec3bbfe0e6f74fc1508adca94c2940a080ed3201011cc8600a4587a4d7b5c118248d435316f6892b60e1c4cc049fed472eb006cabe94d3227a597107ee6ae46ac2b0495da4e2312bd2c2665f1e8f3b3af04bd11268ea793221028ac9a53af4de4469bcb38e6b7805a1bd4534282283baf404766e3aabc57e27eaafe6e3d526ff63ae74ad59ccaba350bdbc329066524225e4c83df6d4976b67c75a0fe0bf26ecb8917a001ff03e5182a561b20265f192fcc1e8913e9b604dd3a8c50dc6de4dcb715aa8c5ed6b1dee520849e74cf1e25c665e7ac8623d5786b551d0e83fdecb7a2e048918f795f25d9245e7c2b4b08639891fa08744c96c8ae610c5e00bbd4c00fb8c78cd5a56a6d9d1c3b91a8aa49d2bc9aa7487ee2675248afd559f2bf2e53241be700c20330aa98139849bbd25fa918c0d00ee715387f388e01ddafa0d9f32f535026c2a1d5e5ca1c7786f70297f692765b5c2060e003779ec678f25b0a8ad3f09003868e31d4ca1490751969755664bf9099ba09af48676c203ef6d893819fe75f9f080a9010cb0641d4108e4a034e1066aea62380082e9462d5dbd9763295152115d3d906e0f81405b67c03040f1fdba9e8a22291cee66a213da00a1a82939ca5585889443c582467dc98c1ec7db2ad0cc5675d6e57fdd11b0d261f95c9c493127173e9abb79820d59d5bc621de7d92f73eb795acc475365f9ec3e00f62de73e8aa17b0d82a2b0704b5f64f67f0a5bdf5b78ac8e7b56323104b97abfa18b8f618b6ff8ac515d30dc7e24688b5bc661dece854e144c2e6844fa0f73151bec43c707b8a904c1ba401150cf476ac133608609a2deeee4be71914f8e2dfa87d9eddab673fcefb8dcc149e3bd4398c3e21fe4f9c94a3a95b0af114e67e4d3256f6ca5769f8d030057a03c8392bee6c04094e843903fdeba4cb6b1200dbbf22b1c057b17d1fc74ebf0e2b948c48cfba494b57cf41d63076d21d63461940d91ad5c7e8b0b9119bd9f070029c395ffb2aa0e6a42c50428ab7b9e4601f855e3b2f5a517b47aaf4530d4c3703d108a6c9ef64f6333c26a8743f82a9de11622f91ba4df5453d0d8232a12f1807cdfe00795b0fce05816ae6000db48ff5a3b377cfc808ae6a34a1b751a2396709e981d204e448bf2944cf4eec8b08c07fe3e7d36d29378fe0aa213d1846767c0cbeda7d1d59676a17858927d239317a93d6abcbf2d85c35bf6887d987f8b1dc1ba48554530c6c891ae05d490a57578b9dd98d2f5ab56ef27e49d60fac56abd727f7d7d9b9edefd4b80adc1d26d96a02055030ae5b8b56f5370ac99312cb9490248fde5d277d0de526e0e8e4502190cd6bb86addadf33ce872838dff1bf67e8322c55d8f29137c13c8028fd4712a8c59ef5357e978b01bfd5de3caa9372195d51392e91f4e30a060d554050d0f5f69017511a188c84eed0eb705862f714268381e81321f0c2fd174a4c84063444dfe47ea087231e50acfe22137c25ce4d6bda02598f4d95b3b644a74e80a2dd2a9cbdfdc60db1cb3d9e81699915e8986c24a992b9c3c317cc4e2323de8a35c71a5b48b39196ae83ebaadc7b89c2614c187c4122b0d2003927f576ddf71f89698fcc08c9164a02dec95f7850e592dcc22c0d5532dafff7ed0586e92406009bf7252044ae37de2de248a0920419ccc9306243bd20d02984910cc33998f22c46a75e769312f46335cbe0839b44dc97f3a8dbe4c9b1eef663adf180e97f4dc8193376f7638a96512f0162bdf052c6f70b68712a9c52394216753b6e40d27875f59202d6169a3be15181c6820fdc55f5520a21feb402ad28a19292df4f84673c3e470e5f4bace0c17074649c80d78a2fceab1837af4286e0556a9b68e99edfda0f1b4d816edb9a17133b306fbd713cc57e73d7fbe4626e8a5db2e9ecefb436349c7c60683cfd01d8c292be9e7be832a93466e72662b06345508f78a1c229cad1d62974be787f3b43446e56c0ec14c7c1916909c59681f510273651cbbfd259282295fe30800d0abc96432b550df4b2cba15971768190a089a294e6395f7db1923b045ba49a9288344abde983e246667136886d1d6451c2e590f932f74a6919e95b43a356b38233212e30eb1ffa92a972031fabaa5992ff19f5e7adcfa424b76e1794930378c2ec7d417b0ba06c0995497b4bfb5193909852443a975b6f2f1eb35f76414bed66cdbb4d78127d777085949554cc066be0a389061a2612de4e3aff95ab471fca7edd7c325e46b6392741d0a8a6384393e1d88ee0ab0217ca4cdd7a540ebc592450a7ccefb601b787b8bb37f87acce0dcf05aa8dab9248671e0c4ae48e68be16c28e9fa7892392c327d920e7f228b1818c0fb6ce357e2a6f432a73b1625270de7598cfad78fcd4ed8cd61e0caeaea73e90025ad2263a0491cad774433a6a2a5603552d2432c3562df1960448cfc5c77e9600a342e5a0fb59d2d3c59a5cad80e7e89a6cde76404a5192b764aa2a5440ca4f2f53b36d1f973eb8a42f76cb8b85a96edbf9ede2a7e45e873caee089870a64d30154122507aef82763f71c119738f8e630ba183c3dad4bd7dcb22e154915ebed2d3049ad556433a3d477113a0d1ec2183cb40ecf9c7b2205a9d2313b9d284f881eaa5a0df0c779a62ec623fa625564c908819871d0eda6ebb87dae27cce9b217108c253412257762231fa826d1ca59d856d2422a3b70ad8db8f835e43facaa7517f412592fd56b55c738e15626155265f5e08a30f9e2380ff939680fd31d5c9818147d43dedd9a75c130e01343ebfcef42f34d9d58a395666159665edf5517a715e11370386245aaf38e979a6de8d004b021644af89e3a8af6a9895e15ffa5a62a85a2ce71fb035b791c43e9fc861c3fc0378cc445354c2aabe5fdc8fd6abc7e2dde7e824d41360f1c664770896d164a66d81f1c2099b852ac1b6231abccfa271554713725102613e592527272ee3de12a518a648515ab7bcbec838cba30bac22645dca6372e6823a08deebe71d5ea98807e9b3ceab9bcb3e98d3d21fb7bc97314374f8736eef149f81d7e60d6d08c3a748759a1e1b25f0d7aa34af578bd8d1909e5ee4dc730b341d2fa5b0f1e1c62d5491d963ef95bf6b074f471cd67bb3be21f25316306a63927aee248ea0e760d9e8991b53ae4344fefc91c6931964a25e70b13c0df04d755b1031a1bb9cc7c0cb893f033119f60ed7916b09a4744a7c4932f9d724b44a7274973743bd5ce061cfe78aaa07e08d9f7a69e8acd260ecd091a1ec7bd6bc2c86edeedea3839063635ccfc602b537edf32df8a6812eca9599b082cf34280289889631b9472c574029ebf387bdb6cbbcbc867e90519cf30e47224167b9eb6869fe71cd56741bfa44aa926f0373e0e53ffbc68d27fe5aaa2f7a7531407a65fe05dcc42adbbc2e15a6afd7a3c78e2f101fb8bc68725bd41f079ee5e2a5d465e57653e462f4cc3ec94e2a007b90caf34b0677c9e56aba559648b740822261f54ac4a38a21b66b734917e2db39284bad9731e58dbb666de753ab11f8121b935c281a326d7fe09dc225c0b5c0b5b0c315ba36d6f9671d83f86c238edc12e2a8ec4ef750cb0d3d098e85595ba077f002771dcab930abbce17624af0fc9803bb78964b8f69e83af710afe5d6cc8f47845633e779372fd4c378ae0a07544714bd1fab35ef607011c6d0928c03ac61f8bb16ef1689c63e520dd1e0061aa08502f49c527327715326a820d485cd07887a0d6e8adcaa8c1c4fb19ca753f2b4911717e8bf9d6de58b0ebfff6e1cb1c119116acfe1139ae905d532195621e8c7811b27a3237feb01af93b31c594c294f44db4b13d52caf64b91b28c85bdf10e3972e3fd306b16506999526687bffcc8b26ebae98d5458b10a56eadeb0359dafc771d5c821d5148fa5058ffd8ad8879ab993239dfa206269faa645b647f130ae331016c801d4200a4177566cc3d3838c45109a407b55de78941ffae8fe6177b1d1210644651ae0a77c1c7ab833a81a97e2b30766308b80045d91a5d4437f6c1b0bf205f4a40802c211764dac2ff9932cb07239480b1c0db1f67befdc739d191fcc40edb870842d4f014faae3b25722c93da7b5356ef9f59d394706fd9b4848e6cdd1f6d981d02c1e5830ae59799d597b732447560c47d82bd2a41ce7c3f119b064d10edee9a05fa19bba76589ab0deaf8706b80a7286618a2c66fe78004183c0fa91651a96260d0d914f3f8f3c43c77163254d0a09b3fc95cc11f92350ad2e33f6c169d8029d882d113f3fa27bcf5d4edadc971370be191b313598bf2649c391016007fb5174ccd52d7012d39d1d861a731cc45b296ba1f6845c912e4211782a09a2e82d38f190f8ffffab2e164ceb7a39337411ba9f57f9ea55d6f215a47a897bc2501d65e640c578dea884cd14c5c854f530741f7e45673d9391a3e40ef11f5080cd5e73f44a0a318bc94065bb85f4bc8bc8220b5dada187ffcac245a5fe5fcd61c0c2688914c38700447fc88588de588e942e4b73a32966af7b994d407f3bf2f1dc0d164711f06e29c2cde0f865e99b56e28f1c6d9b3a8d5976edc08efee5cc029959b87f456b940aff559971f91334860bbd138282f42369a3b9b1a298298201ca74935b9233330ad150343dae999e788a0914e33cd5dbc2125239ced1e0f922e57d857cdc852fbea618373df51bd7eb85d605c0765a81d70e07ff4870fb4512fa199f4529c894809df36783c415b4102b3671c9bfec49b039f6ec3af71a0420c9d9d7da74284cf82394e1fc3cda21c5a6f0b769bd841fcd4ec5cfdd94d18cd1ca649c949279e93aa7985fb1752a6c0535cdcd5290523cfb3b1e1675b9098341052cf410383a5991c77cb7c4c329dd77da3da6df0a086c596134a966dd14f71234e7b68f13b28313ec6e69d74f41532308f6582b6dd4090db9eef2e26b177391d074f90db2eea4083f25148f5585168aa5941b6086e78d1c5d7425329465ac9072d1d51448700c369ffafe578e56577af31dab3eb3581b151fb0c81435e9e451328ba5869ffc8b023bad9485a18be42d9b36f455a2fc8929b98b036257f6e3205b75ffb76fd5e259e348d4ba64aa2862c132fa015c12fe25fc56cbf76b370d51ba172bd0d5f4ac5f2fc2c1e9e33e5281ea25504a3121a5a9055fc28f59044702a750776c47fc301e959e462d940aa019d5d3e6993572d41848668f6687f3c30076bedcdf79e500dd3d6e39fb8dfb771e0c1f61d11a83ffd13deac03879d282d1751f5771ab8236b6303e5ca5ae50e12eaf2c64e02ad5aabe15ff5bbf2f7d20017165e21a1b3c39e90633fd4e52ffd25dabb3c2359a6bcae94177d3b069fb6632c0ec01b6dbd3c695bdce702564f350412c6c7826d036befcc5b1fb5f39735022780f41ab74fca8eb93edac43ce9fcee8979fd9c3fe80894878a0a75c89f962a2583188a91999edb980c6dc1c0b1cddbbfb0dc2f54c1831b97e1f2d58687fe2b0dfd7ed13545140103e66d9ef7bf01ab4c8d3b184295992a34f43683c66f373e29154b5ebcc4f8affbb07b63eae67d86c7e2a58f4c64b6c3e0d5f6e2e2f962860eb40f1c56f6b2cebe7e9af8b9b3ca86c23c3a7bd479a1df867a2dae4b41088c0e02a0e0688ee555038909a12383e469c8b5c8794a23e11b473776ff83ca8ec029961ad53a2b243dbcf6093ec89a9619606791535381bb773aa59d76a0bc0275", + "public_inputs_hex": "0x1d420eaa08a65528f470fd3e2a802913b611527f41fca008a0ad4e974750780303f2ad158d360e938540cb9fc2ecbc5168b0078e536f2a2c847cc131d440ec0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000c6e378e01efa1cb216ab0ced340b44360000000000000000000000000000000097621038a18ea02190913636079ece971a28590517ffbdf7ff33ba52157bce68c37c073712ba7f38d05290ebf864b98223de00e9e865ef8e388eeef184b6f39f6bc05eb3d0c54021d2345bd1729316ef17122a4b371cc1b35e37beb851894eee43c8b48d01f6e641ad9ba6e94da997fb154d92131d18a3d5e38ef60661e890f7ed9c3c630efffcd8e51ee7278f13333e167714b66b4e3e97a46578eceefc9955b033a18e6f8ec8add6f96a917a74414421b654b589d9ba8107e6007b5a8b71a20fb1deff0cf22904da6dd4303e7342972486fb2c422fc657a57fb1fa3501d8a94e68d15348674a20401c431592ca2aaf2dff57e20c39913382bfeaafbe4d2daf8171ebe9a277a845723db5482f9128cd" }, "decryption_aggregator": { - "proof_hex": "0x0000000000000000000000000000000000000000000000043fcf4306e2131cf000000000000000000000000000000000000000000000000c5d67d4dda3528cbf000000000000000000000000000000000000000000000006060d23795c2aa33800000000000000000000000000000000000000000000000000028ba39f65b860000000000000000000000000000000000000000000000003431e382e17ac4ef70000000000000000000000000000000000000000000000021b4003913aea921900000000000000000000000000000000000000000000000c82799839439f89aa00000000000000000000000000000000000000000000000000012d6ba13cd4b700000000000000000000000000000000000000000000000080f5bc15f3c6fe10000000000000000000000000000000000000000000000004914047fa68bd1c0400000000000000000000000000000000000000000000000f6e01eb1aec7be2f20000000000000000000000000000000000000000000000000001f55f29a8b26d00000000000000000000000000000000000000000000000e1f18a47a78f5c09f000000000000000000000000000000000000000000000005849783f26c956b9c00000000000000000000000000000000000000000000000c40e8c3b7b05019240000000000000000000000000000000000000000000000000000b6043b2268b016ecbb30ebefecde0945bf712f33a5e35c605e9140717df8327bb6f5ef69445e0d2588d882879f74634b110eeeabd79da186e72e2bafbf2112924860dbcfdf3c026565a0460bc6cc51c6b1e502aa421fe4416359884492c4229e20ba422abdf41b2b5870384f803e511662e81bacbd584a2d20e25bd8fb8f0fa81166284ee76a1c0dba42cfa440d7ffb3747a6be1cdf331190bb4b1647db3ec65dd98881b1a2f2fe7de0b5b7b7e6fffc7e1dcd6d0a7f3fb92cd074ec90146d9824a3d2c3819ec2682e804a383289c2c9f1eba07cc60c1aff1cb5a6b70be9022d5a7a47e9f8e490c624154e85b5d4fd9b49acb44ca6ac73807ddab9dc20aea1648dd55a2a2cbe2112c1693d6efe05f2d2179bfe2b01d2d969c920d5f342d4a685d6be5b87b5efb1ee16d2b93917750a1dc3e00110ec67da2c9c33cc8ed7e6edef97f126e8ecff128ba7efe8208ae73c22be4d44ba04934379e8eb3fb1dc1c03bfa8b55a3486c5b131f1af9b8e575c05ed7f11c7f427c37e544ffd78db60c7f7b5bbd4da54d96fe002392dba277c15ecff2caa1cbbfa1c0972d054420c5d8655056de823a25f82d1772bcde199450e37df722a55e7dac41a8ee29f07fdf5e9977192943104a95800976f03432f34819ad3555a2bf5d72e9bc63f9d3dfaa9288bdffa426fa258f0e1e671b5758cebbb860ae076f5d73b5d244c234c00e8cb236ea18e6dfb7a6385a2ceb66fb20170a1f3e1904f20a2378844037b1dcadf863f5ef19937364dcc3540e2347c932b8fefb1ec54c8d8e9340e464e115efc55df0e7f56f24e44a8933ba080e285d212dfef11316c6a6f955b9ffca9161b565b183fe3b2819f5eaaada5c06755bf971610b6d70689ea43421cd65c25845da3eabbc145b5f325388497628019fb3cf9bea5175a846b9a4311676de776afdf3a762cc5f00fa4d0c4dfd6804110931e5e2644a05030d33c69fb9f202e3cd2df0341d476aa14e0b4a7d817d572d8b912a899d76b2fd4299063aeb8e49faad0c5f5ea9020cec394e6b0e1e3c9a0006fc8173d358061e3d7ecb78dea762d79411830ef656b635473a7cd16c21121ec372950bbc5726d72ee3f558b3757b55c96b953702a53078156f10783707ef2ac2f9753aa280f1268bcfc51d5de10121343500d12ba3c5f6230a09038cc8631212db3c1be0036f93a42f8a72b1b0f3a175fb9c09c4a31cd3a51d90a1226d6a07e82863c27680a6c050247fbced037c94c32038958b6ddd20115a7c942590c31e77dc1c3f5a6d558f0f0f3181a433a08f7641e3a95703b47e6efd448f0e74fe2d2120c5426c502b3d727e77f80277d8f26607705bd8bc705806a1269768f4c90da9c8d09b0bed9fa2ad2d1913c11030c9c9f9f60dee6cd28733e23e4cdc2717013c759281a65f9c3c13648b4b72430d9b7cb4dd11aa50988a436c113cc112f9042147a805128cf3d0524ab1eca70d990e510686f9d81900d21ea40c4eb8bb071309f1ff7af94fbb0f93e57f882e90bfd5d336d6cb45204d92fc5183aa2da5d92ffd7c5455bdb97f512e60e118641466f139a3926b97a57cec762416b5f039aa266c49d2811485d19e5b8c6d8481def5a7b11b4f6eb0d35a0a934ebdf8e6796f00e4fe8aa6c67a91450e430ec4c6ee2de98eab9780377cb932887af8f7a412aa11c46f63ca887cba374702c014f9181150ea66878ce0bc91126b160e63ad073b1d0ae14032feea6c05413985b0eb692a5b77e2cac56156ff2b5b27245c42913b138e299113f3df18768913b59f15bdefde9c8a535ccb394dc3fca88d77701e260e114f74e35904da232b3161239a94432fc6ed77075589e1d63633ee73427a601923d78b146367f15661597a3bf8bd841f53fdbadceda0f2eb3b9faa4a4351bb304131d0a0cfa4c3956c33343e3a8dec9782b0c12111165539ca255dfbf9a578194039d795df60cb57d147f56442d382c48b64b9cb458db43145a36ef77deedf05dda7c099ccfdbe61ac7687d4c38debfb0f94a862244b520fedc90482aa62521004cb00f3d7dd01f3535b2ebbb943ae0d3d88dba55e4ffcde1b929c4e7bf3f1061d9e25aa39cdfdbd3bb070a330577b342eb8115c2d615e373169af62a93283289bfdd959324811b0f3e3ddd44eb0b9c5cdd7eaae77acc35cbf66799bf6692402f5ef94af7bc25aedbc591d063a5e469a239ce962ad0f484a4e76fd2d4f6d152b35d0eb17c6421293d4c63741ab030a1001d888336e86508ab8b14883d12b771668e1bef33f067dcc1b3eff8730a7ae50bba456a0e0f88d3eb6d9cda64596a02fbc33b4961f1485700545c72d42e5d5f39ae5aa7ceaa64f38730de63b00e5491c72a5d315fa7e6b2f997fb8c32d691022ec660e40c3f79b2e4bb511be4f3e840d4a09bcdb6ef1f63ad37da6601137019a92853119af6d763fe02d9d5da4631b0b2fca10cdac6da89c06471694373ca09119b663aa8f2da003491e3592dd081f2c819ab7e65d912dafe80fbfe0b9d827b3a7d97730ca3997ac5dc303080a9f101be695a1ce3b315c5b47ffc36d829c402e443a78161c63bf19ebf7ebaa362fc71709980d1ed1134fef2ff9ce94dacb7164611513e3efeae83e976603751e74fd1cd0954090ba4339542c911dca7c1e551c1b621fa6e43c3f76af3194bde15ce001879243c4b5d2302612ea4faa1eced4812d0f8a6316825ce217dcbf7f12d932071cdb0dd595478abac873da51a48363ba1bbab6a7c9d33811c71548fd889f5d074019a5fd602e278f49d3a5d78c3eebfd9b15daf24b30eafbb9409820548b5c28d994930e37cfc0ee4f823768bc178f2c3c47c8b0c7f68e18197f4d1c0d2c14296bb2e2d8212c4e41962e429f66ae14fc0b1f5d953fb1a5f5178ad9c3457d20068064f3d23ce29df576e721b4aa8128f2a3a2e0989237b8b4dda0f89087c3252976c194886fdbff8fe106b82fe8cfe21ed4601b6e45d5e7df728f5df112599417f762f1b74d77d040a8ad0e44b935c58f2dfe77aa11f4d20ae469c70b1ea777211f3445db92471b04c84945c86a302d96cbee2ec5da3a441967d52df4ddf5d720fd042f565f49741b508380c4244af3ee17913bae8c9ab320459fb9e5ff6fdd0ac44f8ba61e9e6142cc8e5b29266617f01f913c653d122a01b954c49d601cdc2c5a06c02c720bcc7cf83013663183d11f1e87a2bae50b6521ded294e54bc8f029b1a9f11b22519e4cd52aa674780357b510cbad34face5080d1f612288c27c4291773537798a4fbd7dede6922cae8054535d163609a0d5daa8290c847b682a628798200861f4a7f155b7181b287cb8364a1c034a67e5af9031435f02ae78d0d2064184f366dfedc738ff7893eee8e5dcf0127805d813abcdc7b4a7377e53dcd006e0ac8ea735f93588ea0a71676f3c383859c9beb401c369dd53893251285121cfa07e3616b72cf7fb2edede8a60e8bae1b4d9406e467a75ed676f5c73081a4108b9f31a1dbb2d80e1b4692df14a9ce0f854190ec40a3769c1c738b3a430f5503b15ad4808444dab185ba8e4f56ded101f67a45dd451eadd2395c8c45e2726827ddef201bb70c9c3aa8a54c25d99e908c0ea19b5aa121567061312a5f07f059137b6f541daea8980948fb005bfa6d7f6e369095274292b312f91e641a8b9ea4225d759d940cb55f0a933e46a54d811891c1bf7affd9d037f0667dc58fefd5650fa96cda5692e496cce3fbfcaacf903dfdd71009e62766b6c87f3c5b7222108f0cf144bb82a02a2496ce587a7a23c89452f308e6792254e4d6d8c05b3b2ad825287a68259e53a09024f4f42b7e4c0154e6b67c580cbdb69aed8e412658585d9c16ae813774b518a16cb72e9273072f16fc1e696800108ab5ad68fefb495e92081e9d93ab4c44276fa03bd0ddc6e1cea3826569ca4be8df330d800d3159e9c7b004b02887ac694fdd3c3ca859ed6a55a0dc816f518cd898c788b530c2533c2933151b42e7b40ee1484f3d8bb497312cf4b8aa083dc199675cafde4aa229bb847506888be331d090e12335709ed8fa88d2a3abb1bcad1b9bc912b14163e117f2d6090e580d58e9762e20dc5745277328cbc4fc3e68b13cd5931d13973bcef490492da90e2511ba4f72bf9f5720e54499ce3c0a9bad2a88789682dfbd11fae3dc7f141c57158ac4354f912bd8f94543be5ed9144595eecf5d5b305d5a905aff23301585291ceaaefc89118816b3e053546b4974415cfe209a9ccbcf6b6d27f802e8193e0b015d0b91d9bbc7c16d3f673ec9ceb25f488495cbe9f65e7f64991628d81e4ab818a70c6a1f5c0344f972e3d2580118db18687ac9c61febcd613083e2fb10c96092cc963667d90fc249bb5f0246f77efaa9a624b54810f414a01f285ae91f5ad3517c22645f3a908264c6dff27a5fc3e83513e4dde1b2b4d06e7c0c21212a97c5faac94c396c3eb75609b19a9fdb04d70a404545b7688f3fd1664091e9e01d30bff59042e8eaa895df34bd4c8542de49f23b3b05247e2a60ea7c3746c5c1c82e8522d6fbac54bf45a7b88d466e117d6bbdb3bdb04404be881407c8b347128e4ac64753224c8518132ba80d90e9e36d0660a6420d8ae34883a2d3d3844f71aa13a859439f3ddf740e056cd16330f61f2f5f73491bae01256cc374555869e25aaaed0dd62f477ce2b9fa5dc14b4de837f1fc50e0012ff151c99326b80dc2f08ad72af198ecefe4a5bfc090511a7bd527573e9a71868ae2de0205e2fbec5972f2a267989ab9cefad432cd219000dc7f8ded53bb25544c5eac1c808f9ef87e91e356e43f116f8abf3db5af9c41eed862fb8bbb46ee67a9fe1d14c2b375a4d9c282a9272a09ce77f3a19320181606fa006e4809ead7028014bb9c0b6a272e0b02084794cb26bdb23c1c92e23962a8f80b9347562c76fb9782b08be8f480d4aea0d27502da998f8a4d9521fa0bd2ce332ff8d407451ee3189e4066870ee19128315f0c4621e1fbcbd85523b1b090cace6192c14beb904e071b3ee2c3a51322d9504dc793bce7dc0321b82cb0dec5c082cf850d14e22ef3af036646db4b3d03bb729ed41cb39eb0aceab2405ba0f571af49ff5b88af530fc40aa13614a82d4de4b0b1f3dda6e318d86ffc5181f1ae26c6720d6dfeecfc1d7d428bc80b38889edec04c86df292c6c2397538f796dd154e27f83c090a3918842c43fed2e008760a58004093996bfd42360e58e89a3a837a4f024d9ae4de6e61b9fde360f039ae79142310592732dc009d878c5e24931017ba6e1e127fc916cc487dca718eb0d207461accf461484e7f577dd7a7225398a1a222fbab01fa7b4cb966663decc6f86e911b213dd20e59ecbe262b4f78eed6229f8efcf1181f85bd54056cb9344cef882a0211f77acc433cbc72f660b53633076591101a5debd1c7bb75c477cf57136d912f1172c70849b5c0b2da382b36c1bdb2ec95fea0d963942e49a0bcbdc042fff82a017cc2a52d4e27bb55c769c4c8f25bf40863f646ce50b306e6fccaa3a2bef71bc6ef8cce6579234acca57828c774f693a58271dc056a44bbe3a5711292d86b1bc3f24eacac535dfa3ce20d18337450e14cae37fef5a82affb6bbc9048066902b1445ab1e06c254f4459768a63ee8f16062c8eae5d8eb828f3473f50bd1c4c716dac7dfa5ab8b4ce919e636dcf12a4014526e76d8876e9608df30f9576b5c03127e340a8eacc9bffa81a0a6ab84040c0c51cfbce8d0e898c8468a467c7482a3121bf8817f9896eeb0b803864731ec829f424f7ccec392768c521b4618a6ef3a1f864d74a4a1da3e2f0353e14d3341967d04dbd8b1cc7783f9be1937021b6d010604c7fa35db4d3f9e47cf064b7294832f18521aab04892489887ef26dfbcc3514a06665a91d11641cb70b9ebd9ab5998607a57970b30ed6e1abaa8d9a3d09fc0ba348500a8a70360967e7b2e6506caac7700bb250ae6352a52c80df145e23931ad6bba502bb84c26cc47683d959ab382234d2a6b50288c3a9f57be70087244c00cc036e2ac4050038c045f44e1174ccd51ae2831417eca2ae646ac4dc7aa0531250cc026492f0837dd16254415f01076dede1d0161d12a71091d973065b5b0927cd6e7b6887b563f8e8e38df1a2cf49ce127092ae7f14c16b82eb3bd7c287fe23c93e46dcfff700ddc4db7f0acce19981861fa8741b92a96a89f49d6a2b84641bbba385566e6db0023ca34ee25451f7835e0036587e3cbbfe5c5be17f68df7000bc03f38cbdc90bcf75475ed43684342333c98aa481bdc8933146ae091358ca2458e368f42eec2c19f1adc6c63c133152e072bea6ed1e1a254c040e1f2435ff27a5390a1f10b0afa9098d5b17e8ac84a75e0db6c0bb406d0e3e9c66caf5b32c215216ec3708f27dcb83202ae8c8b1d7832d6eb225a4ff6427d2ecd99fc1017a0e83fbe91a93c8d6ad1bcc9525d7ae40d181022456b13c271a213a7b99f0842317bc405b9565800369c7e9ffde1f1c955b9ee216700826fbee267429b2245e542e5b94f74d0e5d911fd6d861de60a9f9472adbb11091348f7ddd786bdaa3638f200fb4be2cc00aa4bb5754bc487d25643fab3e56ee92601b3d957d9969f0d8751295090f09b4524343335d83d5cb59cbe98b4b30e823fb810141a1db8b1f71032161af45ad8399c25773eef4135083c74ba34ed6e9b3f4ccd8ef4b54bc0db31c136bb4f01423231ec602e6867fb5dd7e2f864bfabd3389e116299ca683d0ba3d167f3caeb2a67f8b70fd97a454cac0decf9f04d6d0821e5304e95aafa64496b81308d84cc84554adff0428ac3e3a774a2bbf5adb3bc4bf670dd3bf87a76b1de22fbc0487db3499fdbfb393424d9c5cb42d832652a6fc484f44bf69307278f9482ab4dccf144cc70309b7d05d855221093137ccf6d29e1b92610d4100b5342a5e2400c944585d2dfff0536223c96d29a24d6b3e9c50c9110a0861c6a1ea7b03801af1f2fb4c61e033a13d3824df9251ea47f41739aa82911366142ecfb7cb757318759290a0356f8624ce873e0b73d1f241762cc941e31fb425c16da148efada514a1e93ec356d035d430e837d2bd1fed9ecfaf76e5cd05eef102799439bfd5fc20a5c7540491780488fdd25770f91535cdbe45cffd1a643d3393c2ff20ae2adb25398281111b51e2b65776c2d724c756ec35997aacdfcf38beb2283c693ab38709cbf556bad78501b91e4f1cc6091157d69450fc37a2063edf6e6a6bd6276915052ab24e1a353629dd9f290ea28da3f7c6267ea34a1e898bd279efead05314f611569998a8ffe0617162dedc7f4a8b77685228bd3665de049cd79924d0874a650138ed3fc5d669eab35ef5bdfe9bda7eaeff28c5432cc4520ff3d59c70407f6e23e3fc2315b7e8950f0d17e4845ba7a6beb01ec848c0a6fe91f99aa47653eac0088cd87378ceb2debee1216f0a3b8dcd699f2adab6bb3bdea59deb6af8b5c30c2bcb893d0ef10dab2ebc892846b6db33ddebb227231a17c6048fa74ee7a741b41b5a262d7db93dc96fe513e5dd0c99b6dd4642e88038d159980292b27b32186313dc8597e5cb810335e3af795e63d11907cab90e7d4c6ada30d42b000c6d438c296ac4d65d5e33bea8bbe7b4a5cd346524c4ba600f30a6847116b9488e4adf341a8d23847c4c67286d0c98fc0ddd2a38379a8b6876c063025972a9b15ffb456c11505c57c8339867f94cbead3d5abe1b40023239a3059b77874c2560f33907142fb27c921b1d03af6980a740462cfad06e7ffe76f8e5e12e59bb75ebeab5b420118fdd54b52424bbdcc621331f7c245bbc0d7b4eaa9d72e58fd40b21e71f558627f775a58502a5871380974b05b73b0ad148ecf99de8dda5c06ca5bd6e0604dc038ba19f12c8c3d2125db1afff20e968de85f6ab442adfc88efbfc9db2ab2c5b0f8fe1650a994f2ec40ae3bdc85a964f349c0529ad0f079f50ea0825ee21805a0c67bfbdd0693878a6f8cfce2ccd0b207f299e10855a7f85a30858d3f2b1d08f0f3084f6a16cae9dfeb648a27dadea1060b707ba1e5c391c47074d2eb57614d50dd5170d37488ff2196a83a2fbaef1b86bc62edbd5290f43132c00b2f3feee1c06717981ddcf1556a368ea88e0ed7e0917d701a94ff2094b41fd5095ee272d8e0dae2ee7650a04d96be40757148a898f64208cbfc043be8f4e78ed5412b9a3d91a777d7ffc8fa796977d83f8747557f7c2ecb1b55fc3ea284aa27e0266801ac81059c18e16b5403cd6a72e8ebed73f27cceba4f1acceae1bef11fdb318866a782eb7ac19600a29eb2526aaf66b2f2c19aebfb169b204ddea661bdb2496dd823f08fe40077c9fc0789e6b255fdf6f504c66dd5788a4cb0fb4b1cae352df34cf3916d124a7f654e6030d923dee25df8bb6ce458e8bba4013fa4a0753c0b468b7c72452a860b8530d332783d11a8dd68841893531fde317fffd7c48cf687d5a05281afb3feba15b0f7a4a93698c66bd046e45c1d45caf4bb242d757935cad8488650dc17c4121cd0a9a26482d62c2d8fa932d55d35bf2740b0f1fdbe7ac2cbd1a7603a8331f04d4514446be3dc504cc93bd9077b19049c7eb8caed8bc1b0b0d72db095a628fda749529e7e816ace5b180d4e1c4976f694e0753b94a26359535cbe525e672ba84b2828e9591ff60308d8269248299d7ce9a3167e391934ff6f9a9c20cb02b80acfd6c2fb601b493bdcf85c28279b3b4ff93701ca5e7c5769b08e6d7087a59ea9816c5d741e423901a061006ba0bcbb3d3e52efa26ae49010eac9aaf13d7c55acff3396b5e084135ed1a839d77aa5b2721adf8f82dd98fbaf497fe7c0cbac63bfa36f446fc0eac7e38904f6341e1492c908ea61b9aa45fc34c535f410a4aa69e11e6bf8db6f7c76a33ed651dbb031dd428abc9caaf72d3b11a5c4d90037aacf2ef9c52e3bb4c090181992129221a6d4f51aa2372e69135d49007397508ea07a57ad9f8d6a6cde315d1a3508867d8b981785965a935741b93fc077452283c34cb8f42bef83f16b50da40b9f7ad1284b86097f4282f311c8e411a1cefe1fe6884b6ad897c5a94e675537a7c72370316c5e81dc79ea6c02ab3f7f5db8d717e1b65dbf02a7e9952306d04bbe149ec0693d4056c54e314a3b1df47bf5e0522648282f42a97a0795bf818a7a5e6b23d809d2b50a22a938d1e6ca54a34f88ee030dec4280569f0c9de1b732b2abe849adc8d3d17c11c614b265f73dc91be83505d75421d8c36a800f9f36974aa23421ca49d4aef71609abc285a1a388fb32550df6ce80be66da801a4e7480d46ea0e5e9d99502f36262d9719d80a9e170d9041ae34238c1c869f7610cd75854b2d3ea32cb7b66688a40f32cfa8d3d810ab1a31e2a643bbb8a85309ee6719083301e02526877c00405e226642024902e68375d1756d87687fbbc344cbbf06fa33e6478df5777c0b315a6a12e78336ff36d99b4221d27f8f2fbe3120e79e072e4ccaf0bd39b96e0ad8adfbe35c8d8097e305a96278800c4551a5c5bb02ab81061b26bc39503fc116a4908b1c1bf3f4a376d504108d514ea4d9e7d05026332025cffba8052964a7f5483be52dd292d5ce87dba2810805b9afb131a08e22e92592637e9544cde42f374c6d19b68e40f55dfa0a3ae27a7731fb7ea0301e0afb15b1767576a8719a646806b834b16b9144d0f076e84232b782ed85f1606d4888008f51e9a11dd1a059e3d32195ce48e2f89484e51c713fd55ebe479825bbabbe4afc094fa4fa57cf797a7f2e68cfc1bbce7d6e16a0210a344b5d40ad3a696a7970363d758038e00d1daaf59ac26cebc9aa26cd5b70a08647d2b39f58ff5f90f97b7dd29a040695ef041b56e7b0ea2e11cd58fcdf9f913e24dd5bc513a9cbd5bdcf2fd9f2390dc816af21190b2ae286406c947c5e9230d1d620b0d6da9d45d5321959f1f1a68d3132d15a6d63c9db114544d286f36730dd550811f27bb81fa9d960323679af5998ff2ca35bb5bab129256929401b5ce06cf4a08a1283dbf1a17ffb32f605c7f867d59c984c19e8ee71745381ec3b1121c9ac918ddf281beca7e65b0a02edc0e093495dc39438b90b578952375c5c8282405c3591ae8b05087da8b63d02892949381a53538020303dccad2c8f354f7ee06f6be0bbc8a9e9ac94b62d7f17cca095ecdfb8d524e589d2526000552540a4517f20bf09584b3188f854be98e36c52df7ad7f2caac301238c6774d439069f6d0c4cffc8ff3314a5edac8b508dfd80c4344d62c58b20097c7f09c884a692aebf207a3f5d4f24dbc711adff884b4476692254bd1c65468ba9af6f09618269385513ef067edadd01488fd08961a820692dfa7c146fc564bee31298e99e70facf472ef5edd30ceccb6509bb6c8b36e0d6aa7caf7ae13d39b03da9de6fb75658c8c60fd102e741097da090dc4fbeebaaf20e09986fba5c2b0688563867ce694584f30aa5549e41a864dc76f9cb46e16965299daeb27097f37c9504b359225090e1cd2119a9239c21218bc5e41527be9e0af64fba84eef0d9ae42eac126a953d6266f03f936235b0a41aa1e90fa49144d65ace190e43c4bfac37f90b4ce7d2a7ae2e80cf3441b45c83689d54c8ea678b55f0371c606c92542a47ca79cabb2a848b4c328a8931ecba586c106bf80eedd0ebbf5513dacea7c721e55d87eb9e9fb2790301c5692614189de796e649f4b1cbe4bdb591ed37bdb60b2888b6dc97b317b90471c50b681838b84b418f86521a3bfa440f93cc750cbd018dee3af17f17dcf8839138e8ce7c1cdf4ea2e6a2d4c73306e3eafc10e1346d1f0d3207a0163319744e82a6cac3587e37bf0b715782e3857d1d33f9b7dae89d768d6099d67b4a8088fb9201d407146252d0c21eafd9064058b57bbef66a660600f7259af3daa9ae54f5917a25cbc22b509da7bcf61d7893916b49102547a36918dd295b364688e24a9f52236962b6d3c4d7f4c720884a66e17711a4c65980b32686e75c2aacf501218b92aa76ea1f17a36d4f4fa62390ae64055246707dd34102b2d669654060b47fd1520b9a97fec0e35191df2192fc484f18d9f1d033b1edd819c76c25387a53e41a2126555fd09f07ebc07017e914cca3ed428574d212d8972223c84850c8d6d4438127d41335492ab521fe12eaccc57d86f489b5986b47d556aa913fa797148c18516391060f38dc28602f6ba7264576da966e1e3bc223586feb02f4d7a46bc6c4426fb3c0a66226ef05c0fc7150e1f93cb7eccf3fc210ba79efdea61948d8d84762f1704114e8d214f766af5050c66c9f4c3650fb0f7f5e703cbb8a20f39cc39481cf7293fb5e42c3ec7f0bd54c722edc383368d99902a3789bb4d2cc813aad2ce045e4621b428b00a31fda45261c00b2d57021a3788742aa01e34669b39aff91507bb0f8c1bcef1a13c62df9de3e3b5321ce04d5d6a649b9146efa63dad75030003d368675c759adbb6257d93437c392fdd59014a8d36363b4465dca69eeb847c242b138e08fca95c703ca16fd5013a086a05b8da3d753ef76f973289a9040c11277d1d29bd91cc0cccd5e3dda88dc9b1da4e3ec1b9d3f19c4f3420d53c704a540d0fca299400df78af938387cd3de12006ee1490c43af52c6d41abf122d801fc1146380d3eb0b489b79b2837c4a32cc7819dab37fe77e61bc3c66d1740a3049413c249513975b8f2c14e2b4ba4dc0e100566cdd44216c3246d213e3b0794142a2fc271d302393b79f5537ab4b6e773c9bebb59d8905236d4ca9d9a4cd2261ace13bdfca0349619b573bf3d9d9472d6120011fbb42805e95f792ef416c40c48d12b7e6c243326b90b87565053d9af997addc85fab2342cbc33a4ab48e9e9d6e6c03ce103989b43389c03fb4372abc67b4b5a7f8e969f06714da30e6e2fd4abc970263d9361ef213f3895ccad00db689edf8953713689ff55a3d88857df96418f80f3b7b782cccfa70134468babdb4365264d3ee33520b528a69370b28b581c4ba1039bd7b1d3ea4092dcd8ba51d1dcf66b0e9c5f7bd65abbf46ec03a2c11ab4b313cc1555bf0198086250dab79aa284be53de9e97d8f77b10076d37fd2586fc0801aa92278061d4d74a4c2327bb0e9fb60054497f2069f3882111851e3bd98e111e8a74585acc65ffad0cc8a2f9636ada525e96aeb41555ec6d95cbc35c7f0b1126febf61577d397dfff012a8a2a2ba0bbc3e346bff4517ffb82bc00218f34a0524275f846dd37a83e31de414c7567d37eae0f205ad5b609b46fd3f15882aa39625ef68e343fe1a24d2afa1181036ada5b05326d9574ed1ac5b80afc7b4cc62b326f5b2dccbbf98d6035c4513c0035ee16e43d1103c8cc9c99854e67097385ab41d08e8e1679f1ac29a79ddf5f048e2fd4af504e440b8426a8701c50d3b75066325635a23c653a00d0cbdcf49e161630d2fc898097c0659ee6bc0b492436e179e126c48725bd68d0a137e650ce703d22705efb8b1b34c5541ad603758ab07b00c155cd691a5170fe43ecfd81acd5d83b73c43d34e7106ea5822943243c8dfb0aa01d8291a2c85f4f3736720a9a4a9fef7245e315b7f056877c2f0784f83c863d90f5a0e575230ef44d9eac80fcd8563f88fc421b32a71fd3bd992ab7040be637d1b7b96a48b6cc3e46a8f412726d81a0f71dda6f61e541015f334561f5ea8f5f50dffef49fc3bb61d6365d4f0656c4acaed1b22ef0f605a3692cf6b2c6409e7262fa5919b268d24cd518c7b0b0bfce0f52cff3430e3acc673b9bceef76dd8ca3712a3c9e0ec13298a1e14bc2c16552c1b2faf1738abb3f14310081c9a295bf210149ce61ab0005ba7b316fe0cc06fd183bffa67c9c2834087c4fce89f944d0052124c65c3174dfd325c017ac48b41beab336f002d852ded401311660139a3b2c811e80b92940a0645dbd238b5986f97c7db3f1d0d0f993227f8f228bf5185075e0d5ab73b10fe7baa0c11c493ee14857248fae31a5584856b343f37a12d52f2182c5146c72d2bf02051af5c1caa8422567fd95f517db9f10f39fc5708e54cc0701cbe6f4556b88bd2551025890388336b1ca7eea6384f5acffaccf30d6efe19c90798b7950ca9d224e0260b9e829c67e369a3516c71cbe7a61d6298d191c90e8d06016db8334d019006c5b96eb38758460acf69fc48db44c032ba9ab6ea87bd59041861986a2f2e00bc684b4d8fda3768075b515de0051cdffb880250b6a221f116f53c7d4176905f62466baf88878854e0362e08f6538c7d8cb15a831f7137db017037ad159d59e1d6c4d17e049240f95ebd7a1375b5a4180a57139fd3b5fd461911925edcb0c0f56ab046085a31edc068b9972e98847326d4e1deb6543879941a4c98a2773587830b03ff781ee11ee55c695af8b876740c8d9cea385c57026e211c03c6f56c393d5685c954b8f15c8c9029a742028af63e6740ef31f528fbf71859b1ae2adbd7e2c1dfbd021fca416afb91d90d39217329217bd19a1f594f980216a12c7b64900752db10e316a778868deda29979ddd21ea57ac38d5a1452e218fd6ad620ffba6ffb5167c3d4f7d0e032fa9a7fb13340190ef68792ed8d909f0902ef3cb391891ee86d04d881f95b67111ab6aec854e716aec6839c7c6ee6e02a64965300938e463bd80f81148e2866d598e6e843e9d4358e602b22660aa6060e9117b91aa7932002ccec550e9e7c58f74c39313db7a2c66bbdc21d53af0e3b246b2fe385873352f19df33b58771673063ca079315826db3d6d2b14c6c7472718a9605d5fea60dc7f671dbd7055fb2e6ee7ca651aadf1482295c13921ce1b3e26818e16427bb2bbd1045da1b8e49e4e3e7d9817026aa5d2f03157626b17a9532da63e4b959b21dd12829c5bde5822056de6e644fc2eacd2d993609bf22b78a50cc023a2f5df4f0ba7448064ce2ec6f5052631c99eab0c66dc208848840733f00e939dd6a730151fe93f7b22655f8c9c443658ba97d6a947809b32ed5510928a2cddc65f807eeaf756acea3fa946c9e05ecc26c68649567d47d66a701f552430214b8173ac6ff586f2754c73f34e024ba6ef907c6e2ec34d83d619980ff4b8e72ce1fb2401f60ba3dbb4d7320768f202bc80968a811ffbe41ee2c0e7e4e9f3071ff4e4f04c8de236dba3638547d57c459c8bdca8616a4a59761480700cadb369218200378fb4db8b25b0d7529f083090a7aa8fb2bf9d1da3e4e66fbf50e262080da0864fbc694139cd3b310bcf206d85b3b97f67cfc0e4455324b7ac1e3c1a711ef87c6b980c5c8cb471c6ac764008f859f06528ca70564c190dd411d45b2bf60a63b2fbf8e5bc7d4f60cf76822c199afeabac46ec638b571b6ffbff2eb203c72de58eb4634937f720fb70c8c37d9f18bdf0e4d18cdd4783aaef321bfd8943211f50104dc0501cfc8e9e2dc443c90290c063028dcf39fae0896f6359bf98c82d00aa9179e566810bf417ad5ee32a551ae96f0870b13dda21cdcbdc7563b320262ee77f55be8f856f5716082c82d13514e14795676ab6f46605b6f3935c3c740903761c60b3dcab05864ec30b350b993d26b3927579587bb91ecc1a9a4090fd832fe502368d2c16d5a190ef0ee84997e5a9878c73aa705283cdebb67a54298da22b886c69c65d60a6dd111d046c1b1c49be26d2e422beca6839024819dbfccf15027203792331b6a713413bd7d6ab58b6533367bae966942128d1b12eb87fb49c1b3ad1a1a34a37ec9e228ec871ece7a964aa11795bd40d1929260abf226aa97a", - "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e441124e9634ac86d5322a89fe272dd95ec8f0910e46f753f89c72c622ae351ae9ed0000000000000000000000000000000020efbc1b4676f6018902362ead77e27d0000000000000000000000000000000060ec24b679943ff559895776f6f11c4429012c2b59ed2e47dbf67687e99852bec5d63ca1680e3ad65cb5c747a9c37eb9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022aadc7250d71617ec5c2f852c2e49ed3180111ef6b130ece8b3504a7e2c3d0372c30f9e94afea47a8631132310e6d72615ccd3227fba92d2ae829172e7bf1c2d089943510caae41bc6adc5e284e6194b797027236225a98865400f364685fd6609bd127e0b1c620665f422cca7d6dbde8d84465e9268825c7e12feeaf3590216000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "proof_hex": "0x00000000000000000000000000000000000000000000000616da7fc6a6d8cf29000000000000000000000000000000000000000000000009587bbfcd4668d960000000000000000000000000000000000000000000000006121d7f725ab890ec000000000000000000000000000000000000000000000000000254c3d3438de000000000000000000000000000000000000000000000000da6daa0c6a5d1b5ee00000000000000000000000000000000000000000000000704ad42ddbc8965a70000000000000000000000000000000000000000000000044dae6888c0469bbc000000000000000000000000000000000000000000000000000038249694578500000000000000000000000000000000000000000000000cbe544e1f83d0969a00000000000000000000000000000000000000000000000f022e5672b0d807a7000000000000000000000000000000000000000000000007ea76b9db2039b97e0000000000000000000000000000000000000000000000000002b08a8ad0db8d00000000000000000000000000000000000000000000000d86835de0e0ee2dd80000000000000000000000000000000000000000000000069601171ff6c2bd9200000000000000000000000000000000000000000000000dc64e0a8399e2eb41000000000000000000000000000000000000000000000000000122eb6e0ea81d1d0f557691796b92e9da20b93e97a0863ab1fe2ee78478842a0c49f45fc99fad1db541b4e5adbd88c9ddba68378f955a0fb8fee6c21ddfc8e2fd73d6ff009a283005d6958a7b2064dcf37a906df83f6046a1c41199c4fcc7b1de51bc48831aa306e4801d58d108d333a100de61c3a7c902d11c47b1f3c7d3a0b25a6ad72132132f09bb7f0634f4b826b2ba79aed873c3f63f0bb72625a7d02b4ccc92408ae597051f526e4e6e94fee05e2ae2d8e4b12904bda66ebd9c9d51e9961cfced09cbe11d880ce4fb8dbfbd641d0c0636c70c7c15bf3e232af15eba8b04b2cd763212bf230710d91859e6bf1de118a016a84a1174041993b37a39e1bc004c1b8da7c3b82b019a2526f1f013dbb5954649960459a4c506bf44865e1d6806947274b51dbd0045c746ab31b273af3beb543fb20fc9cfff80cbe7db0a0b511d2f9b14502ff12f5766c56461c016ee71291019c84e22c333cd16bf76310569b0871fdfc9d31b1da62e4933273fec94c2a3cc680ba04e9b6b233a0d13147367ded65fcd59d53406e9772bf6e4ac857b26dc812bf34a1db26c1b64a233b0260626c2469164115910b03889019586c0037c32ea2286fbe1bb31259f82545e8627c3b9e841226d212afe130cf4a9b76961d989066bbbeb66a469dc0b056c285083d68c76bc2ee0f10f7a0f8441de08dad090e4f7d846c46c311aa23fd6f18f312c62c8b007bd61472a2fa97e9e5e5abbf3e1351c8275791fb7d97b289da247a09935c9d149beb26a1d562f06793f41cf7b646a4e457adf890d6f0bbfd0ac668e53ac69d0eea63b8329280e22aad07e3aed7cb334e20e466b04dcd0ce6085feb9a6e975f08da975cd1d816b4b170d94dabe92ba65bc039266a2814b2a91ff6d9cd59dc8488720894a04abd5ba314a630e81139ae7217116133f998269c10294813aa4775e0b16da5a0d23169f49db334305c3cbad59b33c9c08ca8fa4c5ddc084767a3a1005877fad196bbf853f6103f56544f009d9ae15132acb6b48466e73ae67d8c56770d2b4131997c4345fb6896ba5378db26aa654c77a7115a54cbabcf7acc4d40bfd53de0211274d7054ac3355db0cc69e8800a1e98400c4f70a8c9de3d8dbe179b2d44da025d39a26672fc01702344e9a0658c0b20ebe5a51f8508146f73ff6e1cdf6c2e81cfe68d354f0b72533115a2703b6cc5f3f8dbf779cacfacd8680218727155f0b04ea4cbb18eeb93963d0f419b0b5fa441d4a833c7ad7b0d24b0349478764c6f62fe88e483fb87415b1e327ea905d32ba4ba50397fa0c70425ca096d2311c8b0b154558e2f8ca674b7f71ca9de0c8a93088d9d70a28cbe908285aa453e8379f8d0157cca15c75f28dbfc29e918d2f3a7725b901fd5c757339442272b8e3598d912ed8c10828fc7349d1da35ebaacc78ba86ea2feb693b2de08575d8ec8a80515b2678034c4cb3e15920bc6461a3eec15d9f7c19db25ac3641a2af7839540cd2561645682683c9e16381f3c7eda13f44ff9d6f7ad00a8396d92aea2ea28dea93040360a0de2b389e3b93340a145e97c0ad1a45e8aba6843a4499fbf9a31ad6dcab0dd9a87ea40990c30fc82076461d9f27e407bc86df402f7b38797805c4f388ea242a50f5afc343005ec4fa3d15f37349d5201d60204b3948860efb12aca466de2e07da20d2bee1ff4b716cea16ea40c035b6434208394ef47618c9a05b36b21e04420cee3f04ed87c1f371c2454a90ea9fa4981de27084d981ee935b3956f92c2d9d41e8c45932a7ef1260ee236797594bd3f2617f863d45bbb60820882fdad924cd6ca6c8bad6af9027a28bf69d90ede735d5fe94327977bf453aeb327cb99c16b1f87895cc16b0a86d88b40959d9afa7d5dcafd282cadd2eda3b6828fdd574098fc2bf635fea1852b1d69346c936fcd4e7f0c3073514acfcd9c7330ee361e81c01c84df3070615eb2ac30aeca2f379dbc4f11d734bfeb6529aec25a20b26fd29bb71d9e56779e362feb0c4f8e27798321341a8e8f3d35e67acdc479c642fba17ba87677f815f1f8d15b79cef7e2533f16ec1626277ffa230fd678c3c9b12160bf78e02f78a2d70981a87a4e711c1422fdd536bef214655ab3385b4ac07f1d329cc67e97f5e1c25d04d7d26dfb744efac8ba4787e1f213c4a09145b0bb93b6a04070876e3c7f4e4f0dffcc247525f02d8114e0c8b5228a51726ea1a26ee0855239ec8f411387f77f5cba25a65d6872cbb34a7aee156f43c09b0b5b48fecd1a312c270ca2b8b5482fd832f9099c6438547f93cbf674271b26c23ef795ef582e81a36c5cf6a2564e10891b4ef9b29fce765bbd37c0fe30c95a81c1362399c4116280a46ff3973b0b348f98fa741e42fb67e0a4139da4306469e1bd02d119d28e116127b2be924e581533e1a290e3fa117c0f04d0738dbc69707286e5abfa8ddc405400d1f696a80b5aab65c2e70b13f6ec3ecd951208f975b6c80078d8e484c770a8f69c998648ea766a404486515637864a0663b029c14f33a2bb8a60e2894f02c5c82c192c6c2bf9f20eed8e920b778b3af947df672a8275836e7dfed4cbf662ce92080ab808e197dc14b88d00cb6319b4da02c9807078e38467a7e8d310d9207d13c61a05df7bf7766e5c4e5094f91f3d6078666ff1b7f6a02035143dafaa501d47fc044bfb43d453a18f286f09a28b99c7ea1ca9a3c035e7f2807e8431b660048afe711b39e0ab2714be4cc16e02bbff9485034d9c622be8cac586ac080ac1d76b5418f677e6a83252f7940af0a5daa3eafa21b4c7f812c8d2bb376862d2b1479d741f8dd2b08a6971f6e1a1583b315f6467763dc2167787ccdfdb40672900fe95ce635ba0d06e4ff638f6574dead7ea0290934e40e43e210026b650ca20d0e2d75171be19878f9882d0d1810cc8b6f25156033f014b18333518a1e57cff4245a086e4c21a79596f44871a4665222d28d62ff5b82f73dadab1f20344e2a301233df15fb850fca4f43366886b60dfb0c148e43f2c8e5505761054f031e45992e530a393a66137414996546d93ad7b66a0a86b52f547548eda7c5e9895524b511e5a1c458aaae8d6634de11003153f68f16206876b45e7188e948c3f1daa18525f9048ecbf0206c2f818b3cd6c8f8cbf4f3f3f53408ae779b0a870b51792546102234ba05de1caa383cff3c80772d58d6bf23c4ab08750bf84cffa0e9bf020f19d6a7e461d8404d52ed1bfbb6eb292abe3359d175170898a2480005f9db1abd0a8a310ce3514a6db37e25d075b0f1a0822d78ffeefd3be094301f2c1c6febba07df3bc9e983118eb146c90b068a7c634fba08a71f7e5972f225187a78420bf21a477c7e545db56ad031125723ecf029261e9167e4ced9243047061111c9d89002c6f0d4c6238de97e12c43b89611e498ca6f6d8e2c1d7f04f3424399f4094ab04f76c11ac9856f9164971c2ed828ca4ad43e26981c1132b1289800c2e5f0d5e1c30ca44cdd118597ee0f62df250d260a1eb744d707b82b118b985f1383d477e12acc2957c37c491c396614b2ad84c3969ec1ec4f1c7581c3712a103196948442a6907783e4ddc2a1462e6b13a13f9e217f4faf0361ce35af4ce7b349199ca6208be62a56edd8e4c45d7da43d9fc5acb3ff2c3a1e8ffce95fbafa4da21ead9210ad32d381443055f187a6cb02bb24c4c702af499b4de82124499262dbda32d5000057871f9cc917de7237618da0246598a125df8149b1e1dfaefb350506237b8280517a3cd38ba02d278db56960f92e1cda3d667cce290550ef00cef68cb311c28d981009c19e0c73a175e1f8bc43e7f43177c46940bfb9629b3dc8bc2865f5711618c23138e3ed27b59899f18792d4f410e9cee41597751d50144a0efb7722424247808d0c3c7605a628f75f7b33cee58e4a298f8db7e3556a42de5a519523e19edfbd5551c2412ec0d91b21065eb911c75f0c6580f76fe4ae919dd9e20d90e04ebc9b1779aa7db5c235c15d4d62bb0a614c2ba0ae188bb90c08929ac3425be266e6c0828ac369fe62ef6e37377a346655bb964fcd07085830f8e31f66c247e04086671961a732d6e9824f107aa9cb66132da736c241a93091ce4a5d0328775161a9a5cd0a7510adcda72d5380a8e4261b99479379f9ed014bb36aaf09886352d1750492991f7b968fe80a366b41edbe23fc91064a7b4f8279975369a805a2011d4255af3918a920c857fecb08ca970c3bbc183f7af6cd4ccfbcfae498429d219e4de0ff7b638d612d8a932c88edbdaf91dcda4fadc0dacfb8fd6d8d7a6d5f117feb2e63aa7b37005690a183b7cedd096681e4f472b36b0371e87462f5ad693052dea498ff2ddcbd3bfa0fe4e996a06b9790c26f6870dfc3e18c88981d8f8c214fbbb4970ed74213912e132feedfd355ba5273b00d34bbfa521e5126ee83ac50c6018d032e9b1316bf07d5f6961424bb63041a2c28c55f1c913b15b637204b41fc084c1b1a31602652d016d9178eda06ee7128c09bd600a10d18fd526163f0b081047676d393073b466e12d85711f61038a03e57ab4882971ac99b0d6b2bc0e1b9f299e58527f0f16aaf6871e1db4ee3c5dcf014c2dc65524fb5ea32001e92b2956a53aac0a6512d222970ba9e533b4ef412f10c7eb6d34924f6793bc34685402cdebfa8dfc72a3322f3dc298df0470a4a14b65a2cbd0cde85b0b03915631702e5cf7319251934efdca0c802137e7622627764a0f62e35ddd4fc29ef4624f36221330a033a4d4e45bd32d0e7515516aafac2543bceaf4d88c822a2807c777662cad17ca5faf0c64b97712ae199859317feb0b7a23b9e486edeb09299f8ec8a22d1fd21e145147c31830b3d2a93407687ad92abd6b383db919cd235516e7b45214e830bf52047cffaa97e4fdbffd6cf7cf874d4e71d9ccb92d11867be2577f4d1b61347937a154f0694e2db859286dee84b79269d8b9916562b17ee7e0b103f7068e0ea2e5d9262d13e0e915ceae4c80138a00a157779751498bb2f8f0cff30015fd049acd6daa9fd01a052506bcbd467d78f415b6b30de2ae22f9927017a0c22917f82f9b9c05bf6547479aa55ae683aa7457eb8bfeda7a07e913307f4760440135b956996aded6319d9aa9abe0f76ed72488a3e22473ef0cde7c3736429108095e21cc0b47f3bbc9a0a1da00244efe410bdb1bd733792978b3e361144c3ec927c7cfe7b3fbc8c17738558bc2c90ce2ae06cd4ca92ec2f2ec62a5421a5148e62085eefb1a3092518429b7aeaaa7866b65ec7b74cf4e71696633f53f3ff622592a632e91d5af2332983ae86a98559f65d8f82edd814b7fdc257a8502ba0909071d670f16a22cc60642c30fb9fb7fc98474649efdd11acef790c632579810fbb80f8759feceb7061413e6df2f73cb040cdc5381d4b538866d384c44fae8b3ab330ff0b21753498352e5546550f9b114baea0ba201a86b12ad1d15d04ac3b3057507f7640a2cdb667262b3474a10ef58cf41d2ab58694bb562a2cd8feeab13fcdc1ae46053432cb7c5a671455ef7d8f7d8854fd13f9aad973eec42d9a27d2573162102b926891d0b8b81ff1ec89388965457c077b5e77106d056476a8d69cc035a2a6236cef525b4202ec9b49a7a24d33243c0fd4a57372970a46f7bbb685c8f2b2f02c81eedc45a4ba4ecb5a1601d9266daa5b594dd00dd8c5ac2c98bc6d1397001ccaca031b909879d6f36b49dce0e71c0a1f552d908dd7e18c37b2665cff1bb17fc40b5c7c46fe46827effb3dea634f7493abcaa3f21f974122b393ee3f97340a901c45b90fff46f3ee3a2b88256048094e6fc5d49b8ec566413d651b4bfd481f8bea6c3f158d26f2814a0a21165f5ce25ad588073f7747a302d40898305e5d0fbb41e6583ab90ccf4ade432ec1cf063ed319eeb2b6586abb670a4e8ab4f5d3207aaeb87540df8e47a12e79a8e73b6e9743aae8d5e55aae5da5fc551bdc8c5f150b1b55695d5420881d1aea8e1752398bec30da81de0b0601121045cd1dda9f19f7b5d2266d18c3df2b61d1b471737c2add361519680c8d0c3c151baa9a9c5619cd24214810f337d33af7a5438cb1cb419f4c2e4d5a0827a0e9cf91ff2947c6000ce6c8b213350402828339d996c44c2e7ac96b0452a829022fac28bf9c513e1701296727556febb60ef2acb478eb6d9a0e1eba99466e98aae13867404d352107d66c4708e57fafdc69de80c7817ae046f41cfb2882d3babbf5139a9ae4faa80bebdd2ad2e68bec5fcbbff945c5e7fdfe97bbc3093ba2b1f10c60af59cc60d80e5b6a0eb2cebee9e3537a4eb9f002c108a26738f9bde875b7749a07bac29cf527959a05a688c56c9d390a5dd346626ba04a0363646e6967033b9be18399be09288a4cb2e5a0fa4d656881bba6ed66180b6f4c0cede4ef98e34c5b0857bbc95b2f58bb87ee1c1fd91b5f887521033bc3155654bf8ac1a8c05cebc1f8be7e3a6917485cd6e95d67dc1c766117a3180fbbddaa099949c5bf1151c1cbefa10857b62e9d1116130f8343f772e091058db76e77567417464b0ff93d4585abed3886971f259252fa965f51868653047aee30d5e15c10cec90b2b99109910042dd655cb221cd2533b22397a15f8a4476ae6fd822077f56d3802f01d0b00aecca818ddca1f43544eb3cdf138b816ff5895940945597a7655e30b80c98616ef432c9da35323c7927924b4c1046711099315ec5d58169732b4b75b0a28bc279de6465a42b90ccf643157c4ba6eb5bcdcc7995ca022f0fe91c0eaf1fa910a775e32c70877ce29e5bde84a1189da233e3ace11f96364467be583eef1bfe52b1649a05a8bb44303db325b8fab5908df7878a508c6154937ffeea18cbf43fbf14200106941b8f42de76932b23e689995af9e3d04c0b37765389d1dbd1ad2fd720fb20bc864c025182b41e019aace2abcb7567da8a2449e1887286aeceaccdebf9dac751432f38e2e4ccdca3ac87ee7fad352d7ff41cf060d830a5568773f292d983ed3ea9c87b721b01df39b09be61d87dfac5dd5aee60dcdcfc8bb862c758500b189a324db3262f51d10e38f6d42a7fd9f051204231a06595de9ce681e9966313df7734363ffe1852b06dd157c08d974d7191399967c251acaea70c1376924e9082dd3d6eada315522ab87463acd7faba38e7e2f45677c6d6e2f93d64cd3ea57f855228b29086168d443b8768761cd92dce3ec5e7eb49ce0206c45b0d6c5633a8f7cfe5239b1d2ef1e141cc5a4c73ebd5827d91ea4feae63329ab93aaf49f8621647c7658eeac053de71515e765e1bf41d9e57fd7688cb8978b70edd88cc5f397c16e8bdae86806e7f6a5181704db97138fd3ad30bfe8f3f86cbb665f4a7de7916007e45137ad2edd56a1985f9a10a3df3458a734a9ecae9b3592e43bac49201782855b2155523019488b64b546a7aafc563a9b59f929ee0cf71f5faeb4ba027809a47ea91bc61156c1995627c4998f6ff7e8f59e552aac13a31d70e69b88bb179167f0ccad681dd077952dd05dd90a5cf24f2e5bc1e1915a996366fbd7e356358e0b92108bcc2fa60960482e8b422993ce58bfb7cd058f8e714f1e4d777dd4df6d94d185bb590e76ec3c2c47001f0726c38f0e9d2063f3ef67f0fb63023bd6617a9f53d4ebef1e0280a270fe757aac0af79058d909b8ae0361301fbf4252ec5b19dfcb88c9fb2e453ff90a2a56614dece9e3c3c8872a5116a230c48c7f987f257cda6cb8190c2d11786ae6a859b224093cfc916d924d1dffd0c72c60f04a7a4b525b9de3c0ad217f975e26a887400711a9ad9f1026afadfd263d3bf89dfed2e10939f0b3750912ca93f9498d6ef6f01d557fdb07a8e1bacb8f971f56788cc049b36ebc9944bc164843ed2f857d5b80c2d0e8a54340d6dc5ea6bd33dbf0197ac869cba99390d72ef15e01f0a8bcf394dae09bea03d0b6275f95009008923effce8d6c3fd71d7d05169b021ab02551c8061118dfe49ca2b249b80e1b31b35da1e0bb2986d8fde91e7e932c864120afc164374863379619a5f7f42a733faffb78e3c0307955c30f16aaa12541ee9edbdf1bbada45853c95bb84b6ae8b55727fecfd211aa981feb3195902cdf9c47b466eed7df5c99ed5c1a9986be4bff1f8ba0ca9028dca194a571ccdf56160d35f7db7b9b546457a3f58ee88f804b0af4c9d0ac2197afe5cdca827db53677c6c22cb123f66ed7c9a3a5d66631b4e2d28ec1ac2944dd8928c17b600462658fffb922dd59de1b5c4e3c596b135e5b3ec648c55487c4a4e9d03d0fb0eb1aaf689e5664411d678754c62a9c454cf433246204e7f0d35023465870ee1083f29e501695957c73780f0a97b7fedbc2a9d8659ee308637eeeef72467516b07db68fb78a9a9cd0d7e80ec1855e2370dc2a3fc5da2c00a6f2178d5ef6791a32ca0f2ab588997a20dc02b565f20a93ce84018b4f5e9e91b64f1b8fbb4451072010a3a6d25e1e1269b12967a77469f708063e9bb522a5f044414337ce58745a61bfbc7d14ec22abb3b904e2e7ef401b391fe764815648a73250f96d53b6291d924bd03aa9a5f58d5e893c14850fa85f01e939049ac3a56ace1b73fe06cf1bf4d1ae95413410c335e7dd27b9d0ae5db94283f75ff30557ee6ee8c2facd4fd1cd130640da57669309ad2ee076dcb7b4c485d0a68146b2a687a92c2645fde606d7e29fcd5920546f402de4ab466d0ee17e4dc11a167a17ecb77d8d34b4a67c18e1509ddf08b7a929b2aa92918a0032fa4dbcd8a735a34d11034f82e6f0c111a36490c0d9e41bb0b47ec8bbee1492876a765913a332f93bcc3dab8210c067becc09a0ad7426250cb94602452a54e1c073e2ca216a5f107ce4cfbb6b355d09bd65957285448bd1289902f5e9570e7aa2227117d4db540b9c97d3ed0f759545a81e4d1011380c194c5ea5db9507c00f61b87d20d1220fe4985e2e1d30065a23bd3156f23347b8df7af3454b8739d379debb9a5ab71dc50af6f215ec2a15792ba0450250004b392546927bd58a85055a05eadb613806d86914b6d62bffc79eccb75a7540a5b8d2e55bb8014d6618e71455a8f7b2f7f68dd008ba1cc25d355b90bc3ba1429fb08d96be9b5c21c7999f266cd03b43dd942ea09c55fb5bc11c6c5a174425c2a14c41fabff96d751937819c1ba20020918f9b381431322ef154b7363dd8ba90e7fec6e1072b896789f0f4822c0c147367c3fe82792879a0ab3caa19873431814955df92af2d824e8f1c3e879df0cc0d8a0f5bd76189cf2acc89a777aba170407d7788ecbb0c719fe9a150ad45a5dcc5d8d0a6923b7b27ef6c6b7d61c8d36551f74a4356e4dd1ebcef55bc96b0f6d4ec261619111bbbf7d01cfd4886d1de53623b33764960fed4f0310cf5bc7c27fbd4cc043bcb52e3e5a5855388cccac4c3e2326086873a0072e2bd03e12c78207d91b323343a2be00ca1d316015e6b7b9ae16dc2e72f35764059963da57573115adac29eaae415033e5500b11b392df0090240eedc5326ba3c4de726767dc7cbb6ce92f8cae01a136f39e72c82a7f455def1235497e0d5e3950864202a2f45edf6b800dc64b74b696f2aba673b08e4a2d822a929abc9e6ec8f99049f780d6281f48c9e92978df5d80724eb41681b3d4d2551149adab600d300405f0d78e43bd382f5011ce30554f420273618ed4474aeeba226d1c60827a4975c016ce132ac2bff671715085fa050d9512bc678279dabc7e2937ae6846d97736f3c49eb111407b8a9e9b127e5e2447d4901fc973a421ab3d22eb9ea67ff3094ae76e2adf6da4a0384008de2fd1fc52336fae0c0eb8a1f44109530dec9ba7bf4d1b3d51afae1f5d20392178cdb6f4122e1d9241d3d5b2630d22170047f6dfcf200ad991bccb04299c0400ae830abd161c6f16fd7f5ad7d55e2a91a91c308686453239d3a527c4b1761dfdcbee18db8f02e8efacf3628e99f32ee6e7244763d56e037832853423134a5f8a5641ae4f979e50ca4865ef06643f02fabe75f0cace0c0b039d4452ae0f365ff529891fa3f371e664b389ef7886eb3030b411db22fc59dfab6170e4a1ec4e136124a10896528963dfc5c96ed4259d1332ebc2b184c375fb6481b068abfa1c8d7b863b3c764d18dec95f55a13b807a1ddca78ef66c3f679f534b30384f7a796467db5f4e8595e0500d634744ca3ede0313c61ecc7c3c379c7edd4a9e711aac6dbb1768dd7604ad4ede42a037cda84d13a90845592175da3e3c72f9eff83911da8d0f3279fdb75c5057a140ad1feba217ff17582509379007b855924ad126cea015f23da97b9ff1a85d2e26178e7f492e38990677a0396678b98a389e5050c5a74ce4a769d0f445746bbff937f7c2032cfc6627c5c3af996ef37c2bc10c5a7c94c2c6ce88e04e466b9c01bcdaab60491533b97ef4a264153289662cee698be8ef26b021adcff3be3655a6de1f89d2271992a5af8f646b8d1d1887b122539bf5ff80b0d73282d288a0b7b1f6b15b7177151794da1af79b8a3265adbe34932c3a5ddc7cc991ae4280d005ed69ee78a1f612d55895627f1bbc0425d4f27aea7f2030c68852f2e2a869abc8c656282a69d91f4ce47b4ac4253caf925dd52ff54d1cc443761800deab6659b954208a22c27c09d9bc509923b5af45933add7cd707235c987f02bc0f609b1ca5a831eb61ae990d69687bbcbd8889eeab15fba07d77666bac222248c9a11092cfa1368f7ad93e2c70a325f6f55bfb9c2714850e968c5f9159bbdc4da5b5845ab1f4b196b06b860c8eca51ef7ee7ea1c5ac6741c3b45eeddfa510a3b35c87046b84859d192a7482e3c7825d060b1598765bead1f4993717136208fa56b48f2ce52669d7c2aff3d0bbd8cf1549ca6332ec3df67c16d599ebeba774d36a3c23e47fb3893ba793a9a2825bf3dcd7dfde5259eabdc59b130726508bf5285e8e462de85cc1fce521dd40ba15d9af05a686ca5bc521d4f87492904e9471a27df178a9c32b4cd292debb3083e36159fbd062777ce3a5cf7e42b68cfe357293dccb10a66ad7605de413bb62b339778547ae76ac7a2e1ed9831ef465da55fa64c661288a01252ecd7b9f5a62e5b8687ec4064245e7c14f1b044bfb5833f5f5354a5e52a118f30523e80fef11c4a642b47eabd6c27c18e8834c0aa44ee3353aeca611d41d5a139741f106ea329f5a4ca9b810877f6a0530f157229cbd931de9be6ab060c9c7aedf12a6d31bd1416ac65e49f6fc1ba4fcc40934eed1ed6750feab533cee8d2a5bf77b70860230d4bcc80e332fd6dc8eaa597a64910504bb9f7a5951e0b1cc5549b084e62d3612f42240d9fe81c6c474f9c91184edd5d3d9b5b9211e72d85f6d4307f89b015d01987a2ba529b3e3de4305c8d91a646ccc82a4bedb2a4907e9159c7c99ad0ff59036d6623b151c78f80b23febf74f81d0a92495ef3b64f19e66ed2f41012533980476096427924dc67d52afd687936db302e8bd625ceac02ee8d96da3ffaaf18d0dba69b142c534b2af9d4c54d09bd072b0abf5dd514fc97ba8887ba0a3cd11601669661d81e87b4a811cb36b178dd20b80a0f2a7d30e3cf1be317eca455d578327e0b8517954ae519d46c43b511ff7b5b5faec58ba349aaf5e9869f04c2c67a30474deb6a73b2ebd5ac1134811f1a36c4b25196a574cc0466bae6b3fcdfa67a40beb8730f67992873081dd969bcb5fc718be38b7f2d905e0a956005ef56acc7229696bbf0b5783493ae613da7d540be2d78baa3c0ae8ef5dad872e5ddde1b4801ab09d3a847d4e7b763d045842143da7dd7506cfea1768cf8f6c02498045a5c12e7e1694b77217b83e4398daa91eede9dfd0e756b5454f4a53c44db1ed78b05f180801c4a0e6ecd9dd3c75364074acb511c8c2388bda38e4082a2f52d456d690044ac311feb298ab6d7975886247df78d1f764d91992ed87babb126fc4260470063cc176948b6830efede55d9dd95863e7000b0d6449779f26cc2fe918cbf16b0b156dc79e15d687a3ceb7f9e89f387efa6739bc8694a0663c4ddd1034e732d82d98654f4ab727422694f967ff95c25cdcfb0a7b67a9ddbdb38b02ff739702532bf9776901d83b68bcd265e6b441044b18a466db1744e60f715828741c53b38c2234f0062231eb92cbd23949615f9564eeca7e3d36a66eb8379ef32d66fadfa4142727380dc5ad292c52c7a073b76f8cd789835175390cd2e01c49670d4d66062ff44e2d186ace9c739f41b6027976d14bfc760f07d82c033b950d9239f066c31719f4e87afcdaf9ee8d0278b99a0e3d18849003d3be00c682b37ccc6095110605f89adb2b7f013fce5b354fb68b2b32577fbd6c48a89bc1217c32ba2023b975219cee35356409db3827f407d60777d58e8348a4b165a09faa7e89660e811f2b093603ded70cd3e08e680830d676175ecd7efe2b0425ec8d5d7927f40d04ad5902b02397c2b75482dafe445739acb0e8ac4e53158ebd6e15185513efe9d06f38251951521b1b37ee05c9ecf7335e1d561617b68823ee6ff207eecb139bfaf3c919fb94c51769dd2fcc89cceb51856e10d9739407ced8a9849efdc2411496ccd72ba430028754d7f3e1d7aa46a8a78e0a0b05709edb0612241c0431cb38d32a8c05fba833cbbe1077cbb83aced24898c91227ae2ebb7c185a8d7be7353f8a0cfa2b1e7248dc2974a6e373f4c716681a6f5067fe446777c3d31571ef38e09783501336d31654c7c5d95a34d5671e1079566c33d4f32cc5195d4a999f56be81b1c9041c23e40a59a05ba40da1f97eaa2f83d737c4ee7767a7f75d44f94b08c838cb26c9c73e59f573dd389732d09fd9fe9eae468c6b879ae3d2a9d2afc712a9330a21f7670683303dee05ccfc96beb8f2d894df04693179e2973cef115c09354775010f3c5156faba3e58bbf7bd54bee5e5308a25e94fe3b1147078b6147c753db4288927e082ab365f6cd4f74d0e850a3afcf01629f899df35a216e1d3adfcbecc0b5176ab93d7221145c6d3f2011852b1b0d5e52595b97c6d75e6551b6c018464068a4250a21e45c570aef973ff978e2d0c3d1f32ca88295e8dad9a6b665faa3700a8ff8659e594a167530a95f3f4327d7210e6b5963fc3a3444d16cf05a888dc17cd53ef517b3ca9f2c4da57eea412b405d27341a8f3da610420f8cff8fabcc22ea7167258ec2e0e0153836c5e3fcf168f25aa7172528b7a7a10e6a961520d320657abd3feb900a60de5d920a78a84b6b91bd86a032714f0729541468bc628b42fb2580aa35a66684f00e8aa78ccea16a6e2c2a76962a2c97d2bfdc28c1a14b1029c7b68587d6cec18c23893dfb394fa183bfe597d4d9f41afc074c0eff30ee90ac83636244fc935d52d9f8c32884d8ff049e073a3dbfe2e7f3a9fabe056db851b8a0ab427bd70186dac196be85cdd9cf6a472db35588775a41d8f5bcf95cb1d2a0a34567b854f3d22327387c2b32d30ab6abce341820cb40065f24e9b4bf7a111bc7a424546ad33f509901289798a8b7978dfb8db35dce1f6b3dba62a1c5ecd1b0b6ebca55130fff8bf52b3f525c4b8b490664fccc47262ff3df8b6042652960484ce932170e3593575f723508e98ee25bfd80c7c9d432d952dcd2eca28ab9b2294cc39122d4d6c0eb32e53e70a7e8036c79c0c0dbd3799259c82cfc1c7d0910d399780e2969a47f912e8b716239eede7888a4f541bb71f30133851e10cafa40d3e8226743bae7711324bc4ec129c85002d3083a2af215f38359d37b8d6ece90f6c000b0d56175158febef5a203c3fbf701312bf3d11667ba4c0409642de183162b2a7fd33f67c8f2c4df85c2749615563b0a782dd38927c24f0adb3dcd67a82c8d98745e75c91c8cb14a48a18cdc21c337d33defe7a34ee52d58f2ecb0f33e1b2f7ace0a364d7b999319eac3e703147b0f0289d13b496a5baf1132e2d69fea1c05498e6412f68f8157fd373a87e35aa4e60aed1ca9035054dc4624cdc293e6106ab5b7636b1eb293919de2f9cfa94f8106140a2fede50519fec44ee3b4d12a01f74160bfbe701af451915e10e836bf9dada21bcde42f5c5f62adcb5ddf571b2ed223c42e6671814eb85aabd89968c5d0fd5cd71293c45f665190f6dc4e71161bdb143eae947ac64357958b27ebfd7b7d747c635220135d07e5dff8c97c02671cc1498962fd55a7444e856319b117c443a4ede613035f4771414a54e3a189b7030d05c4fed5241d83bcbb4bfde04683b10d845642ea853252e0588d953592541017b5276b6057e49402142dff4a01b46e0577963f1d46395fdf2d11092f0ffe15681c8e2c65ed60de0c5a4fb3be87a9ae7795bbfe16c96f2a2fe963c12e7eea0f851773e33fd8eba81fcbd8a3f51081726eabd522109b504e688c1367f7a831019103d297cdd5e7d383882d43a1e392dde72d8b45f74206cb3a4af9dd534e881a1c9c5304e1951778074af2513419fcda57d4002f8ee3fa959e69394fe07757026d016f186a85c5dcf4736885598c99220b8e32c16c41fa25e9757c796abf710612a3fdac0fd88526f8fffc53774f41d7d5b985ed1af86b398aed2074aad96d09cd2356845017140ee303a82f21315d018a149cb23f4098c5096334152791f128495dec89751238d8f03030911d3d2975e8c637bc65a3c308ee81ee28022f001befbd0c957929a6cf5e6682b645d001a3ff9a5a118325a69b543d179f878b2d0c7808b8477eff26b1c176fd463289dddecf4765b048bd6cb82468bd00c5fb42", + "public_inputs_hex": "0x1a207628cc6936816ccb62a7b56fdbbf8e975293b677c988644e018fc402e44107940cf3c3a18e9c2b8af0fc91a18c91a709c00a8093bbe606a2e9fd472457bc00000000000000000000000000000000c6e378e01efa1cb216ab0ced340b44360000000000000000000000000000000097621038a18ea02190913636079ece970b3f37898c93078120c11921f007446ceef9867e6da4e341fb3bf8991edcbd630000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000223de00e9e865ef8e388eeef184b6f39f6bc05eb3d0c54021d2345bd1729316ef17122a4b371cc1b35e37beb851894eee43c8b48d01f6e641ad9ba6e94da997fb167714b66b4e3e97a46578eceefc9955b033a18e6f8ec8add6f96a917a74414421b654b589d9ba8107e6007b5a8b71a20fb1deff0cf22904da6dd4303e734297000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" } } } diff --git a/circuits/benchmarks/results_secure_agg/report.md b/circuits/benchmarks/results_secure_agg/report.md index da3a023c0..c9fac38a1 100644 --- a/circuits/benchmarks/results_secure_agg/report.md +++ b/circuits/benchmarks/results_secure_agg/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-22 17:39:45 UTC +**Generated:** 2026-05-23 16:20:50 UTC **Git Branch:** `feat/1549` -**Git Commit:** `f5c2fef8490fc34fe7357743220321af9626c879` +**Git Commit:** `604ef9af71651ffae19546146b78a7940744741f` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -27,7 +27,7 @@ Settings for this benchmark run (integration test + Nargo circuit benches on the | Rayon worker threads | 13 | | CPU cores (host) | 14 | | `dkg_fold_attestation_verifier` (EIP-712) | `0x7969c5eD335650692Bc04293B07F5BF2e7A673C0` | -| Verbose logging (`run_benchmarks.sh --verbose`) | false | +| Verbose logging (`run_benchmarks.sh --verbose`) | true | ### Hardware & software (Nargo / Barretenberg host) @@ -72,40 +72,40 @@ Single-circuit `bb prove` on the benchmark oracle witness (not the integration a | Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | | -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 287764 | 1.47 | 24.77 | 15.88 | -| C1 | 2432074 | 9.59 | 27.85 | 15.88 | -| C2a | 3879330 | 11.23 | 25.90 | 15.88 | -| C2b | 5739750 | 19.89 | 25.99 | 15.88 | -| C3a | 3764144 | 11.76 | 26.38 | 15.88 | -| C3b | 3764144 | 11.76 | 26.38 | 15.88 | -| C4a | 2564001 | 9.72 | 26.12 | 15.88 | -| C4b | 2564001 | 9.72 | 26.12 | 15.88 | -| C5 | 4395328 | 18.36 | 26.08 | 15.88 | -| user_data_encryption | 1678200 | 6.39 | 29.08 | 15.88 | -| C6 | 3001847 | 10.65 | 27.81 | 15.88 | -| C7 | 128310 | 0.59 | 27.36 | 15.88 | +| C0 | 287764 | 1.46 | 26.25 | 15.88 | +| C1 | 2432074 | 9.41 | 26.79 | 15.88 | +| C2a | 1446348 | 5.33 | 26.99 | 15.88 | +| C2b | 2889001 | 10.00 | 27.27 | 15.88 | +| C3a | 3563512 | 11.07 | 27.07 | 15.88 | +| C3b | 3563512 | 11.07 | 27.07 | 15.88 | +| C4a | 1961956 | 5.96 | 26.32 | 15.88 | +| C4b | 1961956 | 5.96 | 26.32 | 15.88 | +| C5 | 3719555 | 10.99 | 26.81 | 15.88 | +| user_data_encryption | 1678200 | 5.81 | 26.25 | 15.88 | +| C6 | 3001847 | 10.43 | 27.26 | 15.88 | +| C7 | 109424 | 0.51 | 26.42 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 10.69 KB | 0.47 KB | 3042585 | 176232 | 3218817 | -| Π_user | 15.88 KB | 0.12 KB | 2972941 | 193372 | 3166313 | -| Π_dec | 10.69 KB | 3.47 KB | 3553811 | 187392 | 3741203 | +| Π_DKG | 10.69 KB | 0.47 KB | 3125282 | 176136 | 3301418 | +| Π_user | 15.88 KB | 0.12 KB | 2973001 | 193312 | 3166313 | +| Π_dec | 10.69 KB | 3.47 KB | 3641070 | 187344 | 3828414 | ### Role / Phase / Activity -| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | -| --------------- | ----- | ----------------------------------------- | -------------- | --------- | ---------- | --------- | -| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 1350.54 s | 127.00 KB | 128.56 KB | -| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 157.91 s | 10.69 KB | 11.16 KB | -| User | P3 | per user input | isolated_nargo | 11.94 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.65 s | 15.88 KB | 16.00 KB | -| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 389.34 s | 10.69 KB | 14.16 KB | -| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 70.08 s | 10.69 KB | 14.16 KB | +| Role | Phase | Activity | Metric | Duration | Proof size | Bandwidth | +| --------------- | ----- | ----------------------------------------- | -------------- | -------- | ---------- | --------- | +| Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 616.80 s | 127.00 KB | 128.56 KB | +| Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 160.99 s | 10.69 KB | 11.16 KB | +| User | P3 | per user input | isolated_nargo | 11.18 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.43 s | 15.88 KB | 16.00 KB | +| Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 186.97 s | 10.69 KB | 14.16 KB | +| Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 50.52 s | 10.69 KB | 14.16 KB | -_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **126.59 s** — -not comparable to P2 wall_clock row above._ +_P2 **tracked_job_wall** sum (ZkDkgAggregation + ZkPkAggregation, parallelizable): **43.96 s** — not +comparable to P2 wall_clock row above._ ## Integration test (`test_trbfv_actor`) @@ -114,72 +114,72 @@ not comparable to P2 wall_clock row above._ | Phase | Metric | Duration (s) | | ------------------------------------------------------------------ | ------------ | ------------ | | Starting trbfv actor test | `wall_clock` | 0.00 | -| Setup completed | `wall_clock` | 3.25 | -| Committee Setup Completed | `wall_clock` | 20.24 | +| Setup completed | `wall_clock` | 2.79 | +| Committee Setup Completed | `wall_clock` | 20.37 | | Committee Finalization Complete | `wall_clock` | 0.00 | -| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 157.91 | -| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 1350.54 | -| E3Request -> PublicKeyAggregated | `wall_clock` | 1357.85 | -| Application CT Gen | `wall_clock` | 7.89 | -| Running FHE Application | `wall_clock` | 0.07 | -| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 70.08 | -| Ciphertext published -> PlaintextAggregated | `wall_clock` | 389.34 | -| Entire Test | `wall_clock` | 1778.65 | +| Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall) | `wall_clock` | 160.99 | +| ThresholdShares -> PublicKeyAggregated | `wall_clock` | 616.80 | +| E3Request -> PublicKeyAggregated | `wall_clock` | 617.32 | +| Application CT Gen | `wall_clock` | 0.35 | +| Running FHE Application | `wall_clock` | 0.00 | +| Aggregator P4: Aggregation pending -> PlaintextAggregated (wall) | `wall_clock` | 50.52 | +| Ciphertext published -> PlaintextAggregated | `wall_clock` | 186.97 | +| Entire Test | `wall_clock` | 827.80 | ### Multithread job timings (`tracked_job_wall`) | Name | Avg (s) | Runs | Total (s) | | ----------------------------- | ------- | ---- | --------- | -| CalculateDecryptionKey | 0.61 | 3 | 1.84 | -| CalculateDecryptionShare | 2.18 | 3 | 6.55 | -| CalculateThresholdDecryption | 1.94 | 1 | 1.94 | -| GenEsiSss | 0.80 | 3 | 2.41 | -| GenPkShareAndSkSss | 1.47 | 3 | 4.41 | -| NodeDkgFold/c2ab_fold | 7.19 | 3 | 21.58 | -| NodeDkgFold/c3a_fold | 51.03 | 3 | 153.08 | -| NodeDkgFold/c3ab_fold | 6.83 | 3 | 20.49 | -| NodeDkgFold/c3b_fold | 55.39 | 3 | 166.16 | -| NodeDkgFold/c4ab_fold | 8.46 | 3 | 25.37 | -| NodeDkgFold/node_fold | 16.32 | 3 | 48.95 | -| ZkDecryptedSharesAggregation | 19.08 | 1 | 19.08 | -| ZkDecryptionAggregation | 50.56 | 1 | 50.56 | -| ZkDkgAggregation | 21.03 | 1 | 21.03 | -| ZkDkgShareDecryption | 52.18 | 6 | 313.05 | -| ZkNodeDkgFold | 145.22 | 3 | 435.65 | -| ZkPkAggregation | 105.56 | 1 | 105.56 | -| ZkPkBfv | 5.97 | 3 | 17.90 | -| ZkPkGeneration | 379.49 | 3 | 1138.47 | -| ZkShareComputation | 101.48 | 6 | 608.87 | -| ZkShareEncryption | 288.67 | 36 | 10392.20 | -| ZkThresholdShareDecryption | 305.78 | 3 | 917.33 | -| ZkVerifyShareDecryptionProofs | 0.11 | 3 | 0.34 | -| ZkVerifyShareProofs | 0.34 | 5 | 1.70 | - -Sum of tracked job wall time: **14474.52 s** — **not** end-to-end latency (jobs run in parallel up -to `BENCHMARK_MULTITHREAD_JOBS`). +| CalculateDecryptionKey | 0.04 | 3 | 0.12 | +| CalculateDecryptionShare | 0.16 | 3 | 0.47 | +| CalculateThresholdDecryption | 0.23 | 1 | 0.23 | +| GenEsiSss | 0.08 | 3 | 0.23 | +| GenPkShareAndSkSss | 0.10 | 3 | 0.31 | +| NodeDkgFold/c2ab_fold | 7.16 | 3 | 21.48 | +| NodeDkgFold/c3a_fold | 57.61 | 3 | 172.82 | +| NodeDkgFold/c3ab_fold | 6.65 | 3 | 19.94 | +| NodeDkgFold/c3b_fold | 50.04 | 3 | 150.12 | +| NodeDkgFold/c4ab_fold | 8.45 | 3 | 25.35 | +| NodeDkgFold/node_fold | 14.89 | 3 | 44.67 | +| ZkDecryptedSharesAggregation | 2.91 | 1 | 2.91 | +| ZkDecryptionAggregation | 47.43 | 1 | 47.43 | +| ZkDkgAggregation | 19.53 | 1 | 19.53 | +| ZkDkgShareDecryption | 21.92 | 6 | 131.52 | +| ZkNodeDkgFold | 144.80 | 3 | 434.40 | +| ZkPkAggregation | 24.44 | 1 | 24.44 | +| ZkPkBfv | 3.56 | 3 | 10.68 | +| ZkPkGeneration | 55.11 | 3 | 165.34 | +| ZkShareComputation | 38.70 | 6 | 232.21 | +| ZkShareEncryption | 121.54 | 36 | 4375.30 | +| ZkThresholdShareDecryption | 99.20 | 3 | 297.59 | +| ZkVerifyShareDecryptionProofs | 0.13 | 3 | 0.38 | +| ZkVerifyShareProofs | 0.31 | 5 | 1.56 | + +Sum of tracked job wall time: **6179.03 s** — **not** end-to-end latency (jobs run in parallel up to +`BENCHMARK_MULTITHREAD_JOBS`). ### NodeDkgFold sub-steps (`tracked_job_wall`, per fold prove) | Step | Avg (s) | Runs | Total (s) | | --------- | ------- | ---- | --------- | -| c2ab_fold | 7.19 | 3 | 21.58 | -| c3a_fold | 51.03 | 3 | 153.08 | -| c3ab_fold | 6.83 | 3 | 20.49 | -| c3b_fold | 55.39 | 3 | 166.16 | -| c4ab_fold | 8.46 | 3 | 25.37 | -| node_fold | 16.32 | 3 | 48.95 | +| c2ab_fold | 7.16 | 3 | 21.48 | +| c3a_fold | 57.61 | 3 | 172.82 | +| c3ab_fold | 6.65 | 3 | 19.94 | +| c3b_fold | 50.04 | 3 | 150.12 | +| c4ab_fold | 8.45 | 3 | 25.35 | +| node_fold | 14.89 | 3 | 44.67 | ### Aggregation jobs (`tracked_job_wall`) | Operation | Avg (s) | Runs | Total (s) | | ---------------------------- | ------- | ---- | --------- | -| ZkDecryptedSharesAggregation | 19.08 | 1 | 19.08 | -| ZkDecryptionAggregation | 50.56 | 1 | 50.56 | -| ZkDkgAggregation | 21.03 | 1 | 21.03 | -| ZkNodeDkgFold | 145.22 | 3 | 435.65 | -| ZkPkAggregation | 105.56 | 1 | 105.56 | +| ZkDecryptedSharesAggregation | 2.91 | 1 | 2.91 | +| ZkDecryptionAggregation | 47.43 | 1 | 47.43 | +| ZkDkgAggregation | 19.53 | 1 | 19.53 | +| ZkNodeDkgFold | 144.80 | 3 | 434.40 | +| ZkPkAggregation | 24.44 | 1 | 24.44 | -Sum of aggregation job tracked time: **631.88 s** (parallel CPU work; not P1/P2 wall clock). +Sum of aggregation job tracked time: **528.70 s** (parallel CPU work; not P1/P2 wall clock). ### Folded on-chain artifacts (exported for Π_DKG / Π_dec gas) diff --git a/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json b/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json index a4403f3c3..5014f0c4f 100644 --- a/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json +++ b/circuits/benchmarks/results_secure_no_agg/crisp_verify_gas.json @@ -1,7 +1,7 @@ { "verify_gas": { "dkg": null, - "user": 2972965, + "user": 2972977, "dec": null }, "source": "folded_proof_export_plus_crisp_verify_test", @@ -46,37 +46,37 @@ "proof_aggregation_enabled": false, "multithread": { "rayon_threads": 13, "max_simultaneous_rayon_tasks": 13, "cores_available": 14 }, "operation_timings": [ - { "name": "CalculateDecryptionKey", "avg_seconds": 0.622842069, "runs": 3, "total_seconds": 1.868526208 }, - { "name": "CalculateDecryptionShare", "avg_seconds": 2.173589861, "runs": 3, "total_seconds": 6.520769583 }, - { "name": "CalculateThresholdDecryption", "avg_seconds": 1.947012875, "runs": 1, "total_seconds": 1.947012875 }, - { "name": "GenEsiSss", "avg_seconds": 0.750157333, "runs": 3, "total_seconds": 2.250472 }, - { "name": "GenPkShareAndSkSss", "avg_seconds": 1.550813236, "runs": 3, "total_seconds": 4.652439708 }, - { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 18.934695375, "runs": 1, "total_seconds": 18.934695375 }, - { "name": "ZkDkgShareDecryption", "avg_seconds": 55.885590437, "runs": 6, "total_seconds": 335.313542626 }, - { "name": "ZkPkAggregation", "avg_seconds": 49.000496459, "runs": 1, "total_seconds": 49.000496459 }, - { "name": "ZkPkBfv", "avg_seconds": 6.074181486, "runs": 3, "total_seconds": 18.222544459 }, - { "name": "ZkPkGeneration", "avg_seconds": 380.694253347, "runs": 3, "total_seconds": 1142.082760041 }, - { "name": "ZkShareComputation", "avg_seconds": 118.562180048, "runs": 6, "total_seconds": 711.373080291 }, - { "name": "ZkShareEncryption", "avg_seconds": 297.629677555, "runs": 36, "total_seconds": 10714.668392001 }, - { "name": "ZkThresholdShareDecryption", "avg_seconds": 299.468916014, "runs": 3, "total_seconds": 898.406748043 }, - { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.096928694, "runs": 3, "total_seconds": 0.290786082 }, - { "name": "ZkVerifyShareProofs", "avg_seconds": 0.295297283, "runs": 5, "total_seconds": 1.476486417 } + { "name": "CalculateDecryptionKey", "avg_seconds": 0.036104014, "runs": 3, "total_seconds": 0.108312043 }, + { "name": "CalculateDecryptionShare", "avg_seconds": 0.155666888, "runs": 3, "total_seconds": 0.467000666 }, + { "name": "CalculateThresholdDecryption", "avg_seconds": 0.234273375, "runs": 1, "total_seconds": 0.234273375 }, + { "name": "GenEsiSss", "avg_seconds": 0.088555027, "runs": 3, "total_seconds": 0.265665083 }, + { "name": "GenPkShareAndSkSss", "avg_seconds": 0.120120347, "runs": 3, "total_seconds": 0.360361042 }, + { "name": "ZkDecryptedSharesAggregation", "avg_seconds": 2.90879575, "runs": 1, "total_seconds": 2.90879575 }, + { "name": "ZkDkgShareDecryption", "avg_seconds": 20.727147222, "runs": 6, "total_seconds": 124.362883334 }, + { "name": "ZkPkAggregation", "avg_seconds": 11.371170708, "runs": 1, "total_seconds": 11.371170708 }, + { "name": "ZkPkBfv", "avg_seconds": 3.53991425, "runs": 3, "total_seconds": 10.61974275 }, + { "name": "ZkPkGeneration", "avg_seconds": 96.487730153, "runs": 3, "total_seconds": 289.463190459 }, + { "name": "ZkShareComputation", "avg_seconds": 50.543073166, "runs": 6, "total_seconds": 303.258439 }, + { "name": "ZkShareEncryption", "avg_seconds": 115.255456694, "runs": 36, "total_seconds": 4149.196440999 }, + { "name": "ZkThresholdShareDecryption", "avg_seconds": 102.677723166, "runs": 3, "total_seconds": 308.0331695 }, + { "name": "ZkVerifyShareDecryptionProofs", "avg_seconds": 0.103283111, "runs": 3, "total_seconds": 0.309849333 }, + { "name": "ZkVerifyShareProofs", "avg_seconds": 0.313393508, "runs": 5, "total_seconds": 1.566967541 } ], - "operation_timings_total_seconds": 13907.008752168, + "operation_timings_total_seconds": 5202.526261583, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { "label": "Starting trbfv actor test", "seconds": 0e-9, "metric": "wall_clock" }, - { "label": "Setup completed", "seconds": 3.289690084, "metric": "wall_clock" }, - { "label": "Committee Setup Completed", "seconds": 20.245420292, "metric": "wall_clock" }, - { "label": "Committee Finalization Complete", "seconds": 0.004953375, "metric": "wall_clock" }, - { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 49.158844, "metric": "wall_clock" }, - { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 1226.639023041, "metric": "wall_clock" }, - { "label": "E3Request -> PublicKeyAggregated", "seconds": 1233.9898995, "metric": "wall_clock" }, - { "label": "Application CT Gen", "seconds": 7.686523, "metric": "wall_clock" }, - { "label": "Running FHE Application", "seconds": 0.075931042, "metric": "wall_clock" }, - { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 19.353939, "metric": "wall_clock" }, - { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 331.860517917, "metric": "wall_clock" }, - { "label": "Entire Test", "seconds": 1597.158072, "metric": "wall_clock" } + { "label": "Setup completed", "seconds": 2.6577995, "metric": "wall_clock" }, + { "label": "Committee Setup Completed", "seconds": 20.181663416, "metric": "wall_clock" }, + { "label": "Committee Finalization Complete", "seconds": 0.001112875, "metric": "wall_clock" }, + { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", "seconds": 11.43493, "metric": "wall_clock" }, + { "label": "ThresholdShares -> PublicKeyAggregated", "seconds": 461.185495792, "metric": "wall_clock" }, + { "label": "E3Request -> PublicKeyAggregated", "seconds": 461.7008915, "metric": "wall_clock" }, + { "label": "Application CT Gen", "seconds": 0.357493625, "metric": "wall_clock" }, + { "label": "Running FHE Application", "seconds": 0.000820875, "metric": "wall_clock" }, + { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", "seconds": 3.068546, "metric": "wall_clock" }, + { "label": "Ciphertext published -> PlaintextAggregated", "seconds": 144.435938625, "metric": "wall_clock" }, + { "label": "Entire Test", "seconds": 629.335317291, "metric": "wall_clock" } ], "folded_artifacts": null }, diff --git a/circuits/benchmarks/results_secure_no_agg/integration_summary.json b/circuits/benchmarks/results_secure_no_agg/integration_summary.json index f6fc72466..57b3f5eb9 100644 --- a/circuits/benchmarks/results_secure_no_agg/integration_summary.json +++ b/circuits/benchmarks/results_secure_no_agg/integration_summary.json @@ -23,96 +23,96 @@ "operation_timings": [ { "name": "CalculateDecryptionKey", - "avg_seconds": 0.622842069, + "avg_seconds": 0.036104014, "runs": 3, - "total_seconds": 1.868526208 + "total_seconds": 0.108312043 }, { "name": "CalculateDecryptionShare", - "avg_seconds": 2.173589861, + "avg_seconds": 0.155666888, "runs": 3, - "total_seconds": 6.520769583 + "total_seconds": 0.467000666 }, { "name": "CalculateThresholdDecryption", - "avg_seconds": 1.947012875, + "avg_seconds": 0.234273375, "runs": 1, - "total_seconds": 1.947012875 + "total_seconds": 0.234273375 }, { "name": "GenEsiSss", - "avg_seconds": 0.750157333, + "avg_seconds": 0.088555027, "runs": 3, - "total_seconds": 2.250472 + "total_seconds": 0.265665083 }, { "name": "GenPkShareAndSkSss", - "avg_seconds": 1.550813236, + "avg_seconds": 0.120120347, "runs": 3, - "total_seconds": 4.652439708 + "total_seconds": 0.360361042 }, { "name": "ZkDecryptedSharesAggregation", - "avg_seconds": 18.934695375, + "avg_seconds": 2.90879575, "runs": 1, - "total_seconds": 18.934695375 + "total_seconds": 2.90879575 }, { "name": "ZkDkgShareDecryption", - "avg_seconds": 55.885590437, + "avg_seconds": 20.727147222, "runs": 6, - "total_seconds": 335.313542626 + "total_seconds": 124.362883334 }, { "name": "ZkPkAggregation", - "avg_seconds": 49.000496459, + "avg_seconds": 11.371170708, "runs": 1, - "total_seconds": 49.000496459 + "total_seconds": 11.371170708 }, { "name": "ZkPkBfv", - "avg_seconds": 6.074181486, + "avg_seconds": 3.53991425, "runs": 3, - "total_seconds": 18.222544459 + "total_seconds": 10.61974275 }, { "name": "ZkPkGeneration", - "avg_seconds": 380.694253347, + "avg_seconds": 96.487730153, "runs": 3, - "total_seconds": 1142.082760041 + "total_seconds": 289.463190459 }, { "name": "ZkShareComputation", - "avg_seconds": 118.562180048, + "avg_seconds": 50.543073166, "runs": 6, - "total_seconds": 711.373080291 + "total_seconds": 303.258439 }, { "name": "ZkShareEncryption", - "avg_seconds": 297.629677555, + "avg_seconds": 115.255456694, "runs": 36, - "total_seconds": 10714.668392001 + "total_seconds": 4149.196440999 }, { "name": "ZkThresholdShareDecryption", - "avg_seconds": 299.468916014, + "avg_seconds": 102.677723166, "runs": 3, - "total_seconds": 898.406748043 + "total_seconds": 308.0331695 }, { "name": "ZkVerifyShareDecryptionProofs", - "avg_seconds": 0.096928694, + "avg_seconds": 0.103283111, "runs": 3, - "total_seconds": 0.290786082 + "total_seconds": 0.309849333 }, { "name": "ZkVerifyShareProofs", - "avg_seconds": 0.295297283, + "avg_seconds": 0.313393508, "runs": 5, - "total_seconds": 1.476486417 + "total_seconds": 1.566967541 } ], - "operation_timings_total_seconds": 13907.008752168, + "operation_timings_total_seconds": 5202.526261583, "operation_timings_metric": "tracked_job_wall", "phase_timings": [ { @@ -122,57 +122,57 @@ }, { "label": "Setup completed", - "seconds": 3.289690084, + "seconds": 2.6577995, "metric": "wall_clock" }, { "label": "Committee Setup Completed", - "seconds": 20.245420292, + "seconds": 20.181663416, "metric": "wall_clock" }, { "label": "Committee Finalization Complete", - "seconds": 0.004953375, + "seconds": 0.001112875, "metric": "wall_clock" }, { "label": "Aggregator P2: PkAggregation pending -> PublicKeyAggregated (wall)", - "seconds": 49.158844, + "seconds": 11.43493, "metric": "wall_clock" }, { "label": "ThresholdShares -> PublicKeyAggregated", - "seconds": 1226.639023041, + "seconds": 461.185495792, "metric": "wall_clock" }, { "label": "E3Request -> PublicKeyAggregated", - "seconds": 1233.9898995, + "seconds": 461.7008915, "metric": "wall_clock" }, { "label": "Application CT Gen", - "seconds": 7.686523, + "seconds": 0.357493625, "metric": "wall_clock" }, { "label": "Running FHE Application", - "seconds": 0.075931042, + "seconds": 0.000820875, "metric": "wall_clock" }, { "label": "Aggregator P4: Aggregation pending -> PlaintextAggregated (wall)", - "seconds": 19.353939, + "seconds": 3.068546, "metric": "wall_clock" }, { "label": "Ciphertext published -> PlaintextAggregated", - "seconds": 331.860517917, + "seconds": 144.435938625, "metric": "wall_clock" }, { "label": "Entire Test", - "seconds": 1597.158072, + "seconds": 629.335317291, "metric": "wall_clock" } ], diff --git a/circuits/benchmarks/results_secure_no_agg/report.md b/circuits/benchmarks/results_secure_no_agg/report.md index 77bac7f12..b3d22c527 100644 --- a/circuits/benchmarks/results_secure_no_agg/report.md +++ b/circuits/benchmarks/results_secure_no_agg/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-05-22 17:39:46 UTC +**Generated:** 2026-05-23 15:56:04 UTC **Git Branch:** `feat/1549` -**Git Commit:** `f5c2fef8490fc34fe7357743220321af9626c879` +**Git Commit:** `604ef9af71651ffae19546146b78a7940744741f` **Committee Size:** `H=3`, `N=3`, `T=1` @@ -74,26 +74,26 @@ Single-circuit `bb prove` on the benchmark oracle witness (not the integration a | Circuit | Constraints | Prove (s) | Verify (ms) | Proof (KB) | | -------------------- | ----------- | --------- | ----------- | ---------- | -| C0 | 287764 | 1.51 | 28.59 | 15.88 | -| C1 | 2432074 | 10.13 | 43.32 | 15.88 | -| C2a | 3879330 | 11.31 | 27.05 | 15.88 | -| C2b | 5739750 | 20.07 | 29.21 | 15.88 | -| C3a | 3764144 | 12.08 | 27.24 | 15.88 | -| C3b | 3764144 | 12.08 | 27.24 | 15.88 | -| C4a | 2564001 | 9.76 | 28.00 | 15.88 | -| C4b | 2564001 | 9.76 | 28.00 | 15.88 | -| C5 | 4395328 | 18.87 | 27.59 | 15.88 | -| user_data_encryption | 1678200 | 6.18 | 28.38 | 15.88 | -| C6 | 3001847 | 10.78 | 27.88 | 15.88 | -| C7 | 128310 | 0.55 | 27.37 | 15.88 | +| C0 | 287764 | 1.43 | 25.44 | 15.88 | +| C1 | 2432074 | 9.38 | 26.85 | 15.88 | +| C2a | 1446348 | 5.27 | 25.40 | 15.88 | +| C2b | 2889001 | 9.76 | 26.04 | 15.88 | +| C3a | 3563512 | 11.04 | 26.42 | 15.88 | +| C3b | 3563512 | 11.04 | 26.42 | 15.88 | +| C4a | 1961956 | 5.97 | 26.68 | 15.88 | +| C4b | 1961956 | 5.97 | 26.68 | 15.88 | +| C5 | 3719555 | 11.05 | 26.65 | 15.88 | +| user_data_encryption | 1678200 | 5.79 | 26.44 | 15.88 | +| C6 | 3001847 | 10.72 | 26.87 | 15.88 | +| C7 | 109424 | 0.52 | 26.65 | 15.88 | ### Artifacts | Artifact | Proof size | Public input size | Verify gas | Calldata gas | Total gas | | -------- | ---------- | ----------------- | ---------- | ------------ | --------- | -| Π_DKG | 15.88 KB | 0.12 KB | N/A | 202300 | N/A | -| Π_user | 15.88 KB | 0.12 KB | 2972965 | 193324 | 3166289 | -| Π_dec | 15.88 KB | 3.25 KB | N/A | 188244 | N/A | +| Π_DKG | 15.88 KB | 0.12 KB | N/A | 197956 | N/A | +| Π_user | 15.88 KB | 0.12 KB | 2972977 | 193300 | 3166277 | +| Π_dec | 15.88 KB | 3.25 KB | N/A | 188136 | N/A | ### Role / Phase / Activity @@ -101,8 +101,8 @@ Single-circuit `bb prove` on the benchmark oracle witness (not the integration a | --------------- | ----- | ----------------------------------------- | -------------- | --------- | ---------- | --------- | | Each ciphernode | P1 | one-time DKG participation (test harness) | wall_clock | 1226.64 s | 127.00 KB | 128.56 KB | | Aggregator | P2 | C5 + Π_DKG fold (aggregator span) | wall_clock | 49.16 s | 15.88 KB | 16.00 KB | -| User | P3 | per user input | isolated_nargo | 12.00 s | 15.88 KB | 16.00 KB | -| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.78 s | 15.88 KB | 16.00 KB | +| User | P3 | per user input | isolated_nargo | 11.17 s | 15.88 KB | 16.00 KB | +| Each ciphernode | P4 | per computation output (C6) | isolated_nargo | 10.72 s | 15.88 KB | 16.00 KB | | Aggregator | P4 | C7 + Π_dec fold (full publish→aggregate) | wall_clock | 331.86 s | 15.88 KB | 19.12 KB | | Aggregator | P4 | C7 + fold only (pending→plaintext span) | wall_clock | 19.35 s | 15.88 KB | 19.12 KB | diff --git a/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh b/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh index 20ecf8ad7..645c7b526 100755 --- a/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh +++ b/circuits/benchmarks/scripts/check_circuit_preset_artifacts.sh @@ -40,6 +40,8 @@ for path in "${MARKERS[@]}"; do fi done +ACTIVE="${BIN}/.active-preset.json" + if [ ! -f "$STAMP" ]; then missing+=("$STAMP") elif ! jq -e --arg p "$PRESET" '.preset == $p' "$STAMP" >/dev/null 2>&1; then @@ -48,6 +50,15 @@ elif ! jq -e --arg p "$PRESET" '.preset == $p' "$STAMP" >/dev/null 2>&1; then exit 1 fi +if [ ! -f "$ACTIVE" ]; then + missing+=("$ACTIVE") +elif ! jq -e --arg p "$PRESET" '.preset == $p' "$ACTIVE" >/dev/null 2>&1; then + echo "Error: circuits/bin was last built for a different preset (see ${ACTIVE})." >&2 + echo " Fast fix (no full recompile if dist is ready):" >&2 + echo " pnpm build:circuits --preset ${PRESET} --skip-if-built --no-clean --no-clean-targets" >&2 + exit 1 +fi + if [ ${#missing[@]} -gt 0 ]; then echo "Error: circuit artifacts for preset '${PRESET}' are missing or stale." >&2 echo " circuits/bin/target reflects the last preset built; dist/circuits// must exist for this mode." >&2 diff --git a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh index bbaa1ef51..7cbdd8a9a 100755 --- a/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh +++ b/circuits/benchmarks/scripts/extract_crisp_verify_gas.sh @@ -118,25 +118,23 @@ else "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" echo " [gas] Build artifacts ready." - # `--check`: verify the committed Honk Solidity verifiers - # (DkgAggregatorVerifier.sol, DecryptionAggregatorVerifier.sol) match the - # current circuits' recursive VKs. Fails loudly on drift; benchmarks must - # not silently rewrite committed contracts. If this errors, run - # `pnpm generate:verifiers --write` and commit the diff before benchmarking. - echo " [gas] Checking Honk Solidity verifiers are in sync with circuit VKs..." + # Align circuits/bin with PRESET_NAME, then verify preset artifacts. + # insecure: also diff committed Honk .sol (pinned to insecure-512). + # secure: committed .sol stay insecure-only; gas replay deploys fresh verifiers from bin. + echo " [gas] Verifying circuit preset '${PRESET_NAME}' (dist stamp + circuits/bin)..." if [ "$VERBOSE" = true ]; then - echo " [gas] [verbose] Running: pnpm generate:verifiers --check --no-compile" + echo " [gas] [verbose] Running: pnpm generate:verifiers --check --no-compile --preset ${PRESET_NAME}" ( cd "$REPO_ROOT" && \ - pnpm generate:verifiers --check --no-compile + pnpm generate:verifiers --check --no-compile --preset "$PRESET_NAME" ) else ( cd "$REPO_ROOT" && \ - pnpm generate:verifiers --check --no-compile >/dev/null + pnpm generate:verifiers --check --no-compile --preset "$PRESET_NAME" ) fi - echo " [gas] Honk verifiers in sync." + echo " [gas] Preset '${PRESET_NAME}' artifacts ready for integration + gas replay." require_preset_artifacts fi @@ -173,6 +171,7 @@ else ( cd "$ENCLAVE_CONTRACTS_DIR" && \ BENCHMARK_RAW_DIR="$RAW_DIR" BENCHMARK_GAS_OUTPUT="$TMP_JSON_ENCLAVE" BENCHMARK_FOLDED_JSON="$TMP_JSON_FOLDED" \ + BENCHMARK_PRESET="$PRESET_NAME" \ pnpm hardhat run scripts/benchmarkGasFromRaw.ts --network hardhat ) 2>&1 | tee "$TMP_LOG_ENCLAVE" ENCLAVE_TEST_EXIT_CODE=${PIPESTATUS[0]} diff --git a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh index d3bfc4b05..68cff81f0 100755 --- a/circuits/benchmarks/scripts/replay_folded_verify_gas.sh +++ b/circuits/benchmarks/scripts/replay_folded_verify_gas.sh @@ -87,12 +87,8 @@ if [ -n "$BUILD_PRESET" ]; then ENSURE_ARGS+=(--force-build) fi "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" - # `--check`: verify the committed Honk Solidity verifiers match the - # current circuits' recursive VKs. Fails loudly on drift; replays must - # not silently rewrite committed contracts. If this errors, run - # `pnpm generate:verifiers --write` and commit the diff. - echo " [replay-gas] Checking Honk Solidity verifiers (pnpm generate:verifiers --check --no-compile)..." - (cd "$REPO_ROOT" && pnpm generate:verifiers --check --no-compile) + echo " [replay-gas] Verifying preset '${BUILD_PRESET}' (dist stamp + circuits/bin)..." + (cd "$REPO_ROOT" && pnpm generate:verifiers --check --no-compile --preset "$BUILD_PRESET") fi fi @@ -102,6 +98,7 @@ echo " [replay-gas] Running Hardhat benchmarkGasFromRaw.ts (folded proofs)..." BENCHMARK_RAW_DIR="$RAW_DIR" \ BENCHMARK_GAS_OUTPUT="$TMP_GAS_PARTIAL" \ BENCHMARK_FOLDED_JSON="$TMP_FOLDED" \ + BENCHMARK_PRESET="${BUILD_PRESET:-insecure-512}" \ pnpm hardhat run scripts/benchmarkGasFromRaw.ts --network hardhat ) diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index 5774b8c0e..a99939275 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -204,7 +204,6 @@ if [ "$SKIP_COMPILE" = false ]; then if "${SCRIPT_DIR}/check_circuit_preset_artifacts.sh" "$PRESET_NAME"; then PRESET_ARTIFACTS_READY=true fi - "${SCRIPT_DIR}/ensure_circuit_preset_built.sh" "${ENSURE_ARGS[@]}" echo "Preflight build complete." echo "" fi diff --git a/packages/enclave-contracts/.gitignore b/packages/enclave-contracts/.gitignore index 26f46e396..7fb3fcbbd 100644 --- a/packages/enclave-contracts/.gitignore +++ b/packages/enclave-contracts/.gitignore @@ -55,3 +55,6 @@ ignition/deployments coverage.json package-lock.json pnpm-lock.yaml + +# Benchmark-generated Honk verifiers (secure-8192 etc.; not committed) +contracts/verifiers/bfv/honk/.benchmark/ diff --git a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts index 89cb00338..ac131125c 100644 --- a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts @@ -4,6 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. import { network } from "hardhat"; +import { execSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; @@ -12,12 +13,104 @@ import { BFV_DKG_H, BFV_PK_SUB_CIRCUIT_VK_HASH_PATHS, BFV_THRESHOLD_T, + REPO_ROOT, bfvDecCommitteeHashIndices, bfvDkgCommitteeHashIndices, committeeHashFromLimbs, readVkRecursiveHash, } from "./utils"; +const CANONICAL_BFV_PRESET = "insecure-512"; +const COMMITTED_HONK_DIR = path.join( + REPO_ROOT, + "packages/enclave-contracts/contracts/verifiers/bfv/honk", +); + +function readBenchmarkPreset(): string { + const fromEnv = process.env.BENCHMARK_PRESET?.trim(); + if (fromEnv) return fromEnv; + const activePath = path.join(REPO_ROOT, "circuits/bin/.active-preset.json"); + if (!fs.existsSync(activePath)) { + return CANONICAL_BFV_PRESET; + } + try { + const active = JSON.parse(fs.readFileSync(activePath, "utf8")) as { + preset?: string; + }; + return active.preset ?? CANONICAL_BFV_PRESET; + } catch { + return CANONICAL_BFV_PRESET; + } +} + +/** + * Committed Honk `.sol` files embed the insecure-512 aggregator VK. Secure benchmark + * proofs need verifiers generated from the active circuits/bin preset. + */ +function ensureHonkVerifierContractDir(preset: string): string { + if (preset === CANONICAL_BFV_PRESET) { + return COMMITTED_HONK_DIR; + } + const benchDir = path.join(COMMITTED_HONK_DIR, ".benchmark", preset); + fs.mkdirSync(benchDir, { recursive: true }); + console.log( + `[benchmarkGasFromRaw] Generating ${preset} Honk verifiers into ${benchDir}...`, + ); + execSync( + [ + "pnpm generate:verifiers", + "--circuits dkg_aggregator,decryption_aggregator", + "--no-compile", + "--write", + `--preset ${preset}`, + `--output-dir ${benchDir}`, + ].join(" "), + { cwd: REPO_ROOT, stdio: "inherit" }, + ); + // Hardhat does not pick up freshly written .sol under honk/.benchmark/ until compile. + execSync("pnpm hardhat compile", { + cwd: path.join(REPO_ROOT, "packages/enclave-contracts"), + stdio: "inherit", + }); + return benchDir; +} + +/** Hardhat `project/` source path for a generated Honk verifier file. */ +function honkContractSource(honkDir: string, name: string): string { + const rel = path.relative( + path.join(REPO_ROOT, "packages/enclave-contracts"), + path.join(honkDir, `${name}.sol`), + ); + return rel.split(path.sep).join("/"); +} + +async function deployHonkAggregator( + ethersLib: Awaited>["ethers"], + honkDir: string, + contractName: "DkgAggregatorVerifier" | "DecryptionAggregatorVerifier", +): Promise { + const solSource = honkContractSource(honkDir, contractName); + const libKey = `project/${solSource}:ZKTranscriptLib`; + const libFactory = await ethersLib.getContractFactory( + `${solSource}:ZKTranscriptLib`, + ); + const lib = await libFactory.deploy(); + await lib.waitForDeployment(); + const libAddress = await lib.getAddress(); + + const aggFactory = await ethersLib.getContractFactory( + `${solSource}:${contractName}`, + { + libraries: { + [libKey]: libAddress, + }, + }, + ); + const agg = await aggFactory.deploy(); + await agg.waitForDeployment(); + return agg.getAddress(); +} + function findRawJson(rawDir: string, fragment: string): any { const entries = fs.readdirSync(rawDir).filter((f) => f.endsWith(".json")); for (const f of entries) { @@ -112,10 +205,11 @@ async function main() { ); } else { const folded = JSON.parse(raw); - dkgProofHex = folded?.dkg_aggregator?.proof_hex; - dkgPublicHex = folded?.dkg_aggregator?.public_inputs_hex; - decProofHex = folded?.decryption_aggregator?.proof_hex; - decPublicHex = folded?.decryption_aggregator?.public_inputs_hex; + const artifacts = folded?.folded_artifacts ?? folded; + dkgProofHex = artifacts?.dkg_aggregator?.proof_hex; + dkgPublicHex = artifacts?.dkg_aggregator?.public_inputs_hex; + decProofHex = artifacts?.decryption_aggregator?.proof_hex; + decPublicHex = artifacts?.decryption_aggregator?.public_inputs_hex; } } else { const dkgRaw = findRawJson(rawDir, "threshold_pk_aggregation"); @@ -188,38 +282,24 @@ async function main() { const abiCoder = ethers.AbiCoder.defaultAbiCoder(); - const libFactory = await ethers.getContractFactory( - "contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib", - ); - const zkTranscriptLib = await libFactory.deploy(); - await zkTranscriptLib.waitForDeployment(); - const zkTranscriptLibAddress = await zkTranscriptLib.getAddress(); + const benchmarkPreset = readBenchmarkPreset(); + const honkDir = ensureHonkVerifierContractDir(benchmarkPreset); + if (benchmarkPreset !== CANONICAL_BFV_PRESET) { + console.log( + `[benchmarkGasFromRaw] Using preset ${benchmarkPreset} Honk verifiers (not committed insecure-512 .sol).`, + ); + } - const dkgAggFactory = await ethers.getContractFactory( + const dkgAggAddress = await deployHonkAggregator( + ethers, + honkDir, "DkgAggregatorVerifier", - { - libraries: { - "project/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib": - zkTranscriptLibAddress, - }, - }, ); - const dkgAgg = await dkgAggFactory.deploy(); - await dkgAgg.waitForDeployment(); - const dkgAggAddress = await dkgAgg.getAddress(); - - const decAggFactory = await ethers.getContractFactory( + const decAggAddress = await deployHonkAggregator( + ethers, + honkDir, "DecryptionAggregatorVerifier", - { - libraries: { - "project/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol:ZKTranscriptLib": - zkTranscriptLibAddress, - }, - }, ); - const decAgg = await decAggFactory.deploy(); - await decAgg.waitForDeployment(); - const decAggAddress = await decAgg.getAddress(); const bfvPk = await ( await ethers.getContractFactory("BfvPkVerifier") @@ -323,6 +403,7 @@ async function main() { dec: Number(decGas), }, source: "benchmark_raw_artifacts", + bfv_preset: benchmarkPreset, }; fs.writeFileSync(outputPath, JSON.stringify(output, null, 2)); } diff --git a/scripts/build-circuits.ts b/scripts/build-circuits.ts index 12169112c..232a8e79e 100644 --- a/scripts/build-circuits.ts +++ b/scripts/build-circuits.ts @@ -57,6 +57,8 @@ interface BuildOptions { clean?: boolean noCleanTargets?: boolean skipIfBuilt?: boolean + /** Copy dist/circuits// artifacts into circuits/bin without nargo compile. */ + hydrateBinOnly?: boolean dryRun?: boolean preset?: CircuitPreset | 'all' } @@ -177,13 +179,67 @@ class NoirCircuitBuilder { writeFileSync(this.presetStampPath(preset), JSON.stringify(stamp, null, 2) + '\n') } - /** Marker files required by `test_trbfv_actor` / gas extraction (dist + circuits/bin targets). */ - private requiredPresetMarkers(preset: string): string[] { + /** Records which BFV preset last populated `circuits/bin/` (used by benchmark gas extraction). */ + private writeActiveBinPresetStamp(preset: string, sourceHash: string): void { + const stamp: PresetBuildStamp = { + preset, + sourceHash, + builtAt: new Date().toISOString(), + } + writeFileSync(join(this.circuitsDir, '.active-preset.json'), JSON.stringify(stamp, null, 2) + '\n') + } + + /** + * Point circuits/bin at a preset already archived under dist/circuits//. + * Used when dist is fresh but bin still holds another preset (common after --mode insecure + * then --mode secure benchmark runs). + */ + private hydrateBinFromDist(preset: string, sourceHash: string): void { + const distRoot = join(this.options.outputDir!, preset) + const circuits = this.discoverCircuits() + let copied = 0 + + for (const circuit of circuits) { + const packageName = this.getPackageName(circuit.path) + const targetDir = join(circuit.path, 'target') + mkdirSync(targetDir, { recursive: true }) + + const copyPair = (from: string, to: string) => { + if (!existsSync(from)) return + copyFileSync(from, to) + copied++ + } + + const defaultDir = join(distRoot, CIRCUIT_VARIANTS.DEFAULT, circuit.group, circuit.name) + copyPair(join(defaultDir, `${packageName}.json`), join(targetDir, `${packageName}.json`)) + copyPair(join(defaultDir, `${packageName}.vk`), join(targetDir, `${packageName}.vk_recursive`)) + copyPair(join(defaultDir, `${packageName}.vk_hash`), join(targetDir, `${packageName}.vk_recursive_hash`)) + + const evmDir = join(distRoot, CIRCUIT_VARIANTS.EVM, circuit.group, circuit.name) + copyPair(join(evmDir, `${packageName}.vk`), join(targetDir, `${packageName}.vk`)) + copyPair(join(evmDir, `${packageName}.vk_hash`), join(targetDir, `${packageName}.vk_hash`)) + + const recursiveDir = join(distRoot, CIRCUIT_VARIANTS.RECURSIVE, circuit.group, circuit.name) + copyPair(join(recursiveDir, `${packageName}.vk`), join(targetDir, `${packageName}.vk_noir`)) + copyPair(join(recursiveDir, `${packageName}.vk_hash`), join(targetDir, `${packageName}.vk_noir_hash`)) + } + + console.log(` Copied ${copied} artifact file(s) into circuits/bin targets.`) + this.writeActiveBinPresetStamp(preset, sourceHash) + } + + private requiredDistMarkers(preset: string): string[] { const dist = join(this.options.outputDir!, preset) - const bin = this.circuitsDir return [ join(dist, CIRCUIT_VARIANTS.DEFAULT, CIRCUIT_GROUPS.AGGREGATION, 'dkg_aggregator', 'dkg_aggregator.json'), join(dist, CIRCUIT_VARIANTS.DEFAULT, CIRCUIT_GROUPS.AGGREGATION, 'decryption_aggregator', 'decryption_aggregator.json'), + ] + } + + /** Marker files required by `test_trbfv_actor` / gas extraction under circuits/bin. */ + private requiredBinMarkers(): string[] { + const bin = this.circuitsDir + return [ join(bin, CIRCUIT_GROUPS.AGGREGATION, 'dkg_aggregator', 'target', 'dkg_aggregator.json'), join(bin, CIRCUIT_GROUPS.AGGREGATION, 'dkg_aggregator', 'target', 'dkg_aggregator.vk_recursive'), join(bin, CIRCUIT_GROUPS.AGGREGATION, 'decryption_aggregator', 'target', 'decryption_aggregator.json'), @@ -193,10 +249,30 @@ class NoirCircuitBuilder { ] } - private isPresetUpToDate(preset: string, sourceHash: string): boolean { + private readActiveBinPreset(): PresetBuildStamp | null { + const activePath = join(this.circuitsDir, '.active-preset.json') + if (!existsSync(activePath)) return null + try { + return JSON.parse(readFileSync(activePath, 'utf-8')) as PresetBuildStamp + } catch { + return null + } + } + + private isDistPresetUpToDate(preset: string, sourceHash: string): boolean { const stamp = this.readPresetStamp(preset) if (!stamp?.sourceHash || stamp.sourceHash !== sourceHash) return false - return this.requiredPresetMarkers(preset).every((path) => existsSync(path)) + return this.requiredDistMarkers(preset).every((path) => existsSync(path)) + } + + private isBinReadyForPreset(preset: string, sourceHash: string): boolean { + const active = this.readActiveBinPreset() + if (!active || active.preset !== preset || active.sourceHash !== sourceHash) return false + return this.requiredBinMarkers().every((path) => existsSync(path)) + } + + private isPresetUpToDate(preset: string, sourceHash: string): boolean { + return this.isDistPresetUpToDate(preset, sourceHash) && this.isBinReadyForPreset(preset, sourceHash) } private logSkipIfBuiltBlocked(preset: string, sourceHash: string): void { @@ -212,7 +288,7 @@ class NoirCircuitBuilder { `Run without --skip-if-built or \`pnpm build:circuits --preset ${preset}\` once to refresh.`, ) } - const missing = this.requiredPresetMarkers(preset).filter((path) => !existsSync(path)) + const missing = [...this.requiredDistMarkers(preset), ...this.requiredBinMarkers()].filter((path) => !existsSync(path)) if (missing.length > 0) { console.log(` ℹ️ --skip-if-built: missing ${missing.length} marker artifact(s), e.g. ${missing[0]}`) } @@ -244,14 +320,35 @@ class NoirCircuitBuilder { const sourceHash = this.computeSourceHash(preset) result.sourceHash = sourceHash + if (this.options.hydrateBinOnly) { + if (!this.isDistPresetUpToDate(preset, sourceHash)) { + throw new Error( + `Cannot hydrate circuits/bin: dist/circuits/${preset} is missing or stale. ` + `Run: pnpm build:circuits --preset ${preset}`, + ) + } + console.log(` 💧 Hydrating circuits/bin from dist/circuits/${preset} (no nargo compile)...`) + this.hydrateBinFromDist(preset, sourceHash) + console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}`) + return result + } + if (this.options.skipIfBuilt) { if (this.isPresetUpToDate(preset, sourceHash)) { console.log( - ` ⏭️ Skipping preset ${preset} (artifacts up to date; source_hash=${sourceHash}). ` + + ` ⏭️ Skipping preset ${preset} (dist + circuits/bin up to date; source_hash=${sourceHash}). ` + `Use a full rebuild without --skip-if-built to refresh.`, ) return result } + if (this.isDistPresetUpToDate(preset, sourceHash)) { + console.log( + ` 💧 dist/circuits/${preset} is current; hydrating circuits/bin from dist ` + + `(fast — avoids a full ~50m secure recompile when switching presets).`, + ) + this.hydrateBinFromDist(preset, sourceHash) + console.log(`\n✅ Hydrated circuits/bin for preset: ${preset}`) + return result + } this.logSkipIfBuiltBlocked(preset, sourceHash) } @@ -280,6 +377,7 @@ class NoirCircuitBuilder { this.copyArtifacts(result.compiled, presetOutputDir, preset) if (result.errors.length === 0) { this.writePresetStamp(preset, sourceHash) + this.writeActiveBinPresetStamp(preset, sourceHash) } console.log(`\n✅ Built ${result.compiled.length} circuits for preset: ${preset}`) if (result.errors.length > 0) { @@ -757,6 +855,7 @@ async function main() { else if (arg === '--no-clean') options.clean = false else if (arg === '--no-clean-targets') options.noCleanTargets = true else if (arg === '--skip-if-built') options.skipIfBuilt = true + else if (arg === '--hydrate-bin-only') options.hydrateBinOnly = true else if (arg === '--group') options.groups = args[++i]?.split(',') as CircuitGroup[] else if (arg === '--circuit') (options.circuits ??= []).push(args[++i]) else if (arg === '-o' || arg === '--output') options.outputDir = resolve(args[++i]) @@ -800,7 +899,8 @@ Options: --dry-run Show what would be built --no-clean Don't clean output directory --no-clean-targets Don't delete circuits/bin target dirs before compiling - --skip-if-built Skip preset when dist stamp + marker artifacts match circuit sources + --skip-if-built Skip preset when dist + circuits/bin match; hydrate bin from dist if only dist is current + --hydrate-bin-only Copy dist/circuits// into circuits/bin (no nargo compile) -h, --help Show help `) } diff --git a/scripts/generate-verifiers.ts b/scripts/generate-verifiers.ts index 3a8f93de9..fba43940f 100644 --- a/scripts/generate-verifiers.ts +++ b/scripts/generate-verifiers.ts @@ -37,7 +37,7 @@ import { execFileSync, execSync } from 'child_process' import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'fs' import { basename, join, resolve } from 'path' -import { ALL_GROUPS, CIRCUIT_GROUPS, type CircuitGroup } from './circuit-constants' +import { ALL_GROUPS, ALL_PRESETS, CIRCUIT_GROUPS, type CircuitGroup } from './circuit-constants' // --------------------------------------------------------------------------- // Types & constants @@ -93,6 +93,10 @@ interface GenerateOptions { * verifier contracts. */ check?: boolean + /** BFV preset whose artifacts in `circuits/bin/` are used for generation/check. */ + preset?: string + /** Override output directory (write mode only). Defaults to committed honk/ path. */ + outputDir?: string } // --------------------------------------------------------------------------- @@ -108,7 +112,10 @@ class VerifierGenerator { constructor(rootDir?: string, options: GenerateOptions = {}) { this.rootDir = rootDir ?? resolve(__dirname, '..') this.circuitsDir = join(this.rootDir, 'circuits', 'bin') - this.verifierDir = join(this.rootDir, 'packages', 'enclave-contracts', 'contracts', 'verifiers', 'bfv', 'honk') + this.verifierDir = + options.outputDir !== undefined + ? resolve(options.outputDir) + : join(this.rootDir, 'packages', 'enclave-contracts', 'contracts', 'verifiers', 'bfv', 'honk') this.options = { groups: ALL_GROUPS, clean: false, @@ -124,13 +131,22 @@ class VerifierGenerator { this.checkTool('nargo --version', 'nargo') this.checkTool('bb --version', 'bb') - // Refuse to run unless `circuits/bin/` was last built for the canonical preset. - // This is the only on-disk witness of "which preset's VKs live under circuits/bin" - // (cf. `scripts/build-circuits.ts:writePresetStamp`). Skip the gate in --dry-run mode - // (no artifact reads will happen) and in --no-compile mode only if explicitly allowed - // by a future opt-out — today, even --no-compile must run against the canonical preset. + const targetPreset = this.targetPreset() + if (!this.options.dryRun) { - this.assertCanonicalPresetBuilt() + this.assertPresetBuilt(targetPreset) + this.assertCircuitsBinActivePreset(targetPreset) + } + + // Committed Honk `.sol` files are pinned to CANONICAL_PRESET only. Secure (and other) + // benchmark modes still need `circuits/bin/` aligned to their preset for integration + gas replay. + if (this.options.check && targetPreset !== CANONICAL_PRESET) { + console.log( + `\n✅ Preset '${targetPreset}' is built and active in circuits/bin.\n` + + ` Committed Honk verifiers in git are pinned to '${CANONICAL_PRESET}' only; skipping .sol diff.\n` + + ` Benchmark gas replay deploys fresh aggregator verifiers from circuits/bin at runtime.\n`, + ) + return } const circuits = this.discoverCircuits() @@ -485,34 +501,22 @@ class VerifierGenerator { } } + private targetPreset(): string { + return this.options.preset ?? CANONICAL_PRESET + } + /** - * Refuse to run unless `circuits/bin/` was last built for `CANONICAL_PRESET`. - * - * The committed Honk Solidity verifiers correspond to one preset - * (`CANONICAL_PRESET`). The only on-disk record of "which preset built - * `circuits/bin/`" is the build stamp written by - * `scripts/build-circuits.ts:writePresetStamp` at - * `dist/circuits//.build-stamp.json`. If the stamp for `CANONICAL_PRESET` - * is missing, the developer either never built it, or last built a different - * preset — either way, generating/checking against `circuits/bin/` would use - * the wrong VKs. Surface that loudly with a fix recipe instead of silently - * producing wrong verifier bytes. + * Refuse to run unless `dist/circuits//.build-stamp.json` exists for the target preset. */ - private assertCanonicalPresetBuilt(): void { - const stampPath = join(this.rootDir, 'dist', 'circuits', CANONICAL_PRESET, '.build-stamp.json') + private assertPresetBuilt(preset: string): void { + const stampPath = join(this.rootDir, 'dist', 'circuits', preset, '.build-stamp.json') if (!existsSync(stampPath)) { throw new Error( - `Canonical preset '${CANONICAL_PRESET}' is not built (missing ${stampPath}).\n` + - ` The committed Solidity Honk verifiers under\n` + - ` packages/enclave-contracts/contracts/verifiers/bfv/honk/ bake in the recursive VKs\n` + - ` of the '${CANONICAL_PRESET}' BFV preset. Generating/checking against any other preset\n` + - ` would produce different '.sol' bytes and is rejected by design.\n` + + `Preset '${preset}' is not built (missing ${stampPath}).\n` + `\n` + ` To fix, run from the repo root:\n` + - ` pnpm build:circuits --preset ${CANONICAL_PRESET}\n` + - ` then retry. If you intentionally want verifiers for a different preset (e.g. a\n` + - ` production deploy on 'secure-8192'), generate them locally for that deploy — do\n` + - ` NOT commit the result over the canonical files.`, + ` pnpm build:circuits --preset ${preset}\n` + + ` then retry.`, ) } let stamp: { preset?: string } = {} @@ -521,16 +525,49 @@ class VerifierGenerator { } catch (err: any) { throw new Error(`Failed to parse ${stampPath}: ${err.message}`) } - if (stamp.preset !== CANONICAL_PRESET) { + if (stamp.preset !== preset) { throw new Error( - `Build stamp at ${stampPath} reports preset '${stamp.preset ?? '(missing)'}', expected '${CANONICAL_PRESET}'.\n` + - ` The committed Solidity Honk verifiers correspond to '${CANONICAL_PRESET}' only.\n` + + `Build stamp at ${stampPath} reports preset '${stamp.preset ?? '(missing)'}', expected '${preset}'.\n` + ` Run:\n` + - ` pnpm build:circuits --preset ${CANONICAL_PRESET}\n` + + ` pnpm build:circuits --preset ${preset}\n` + ` then retry.`, ) } - console.log(` ✓ Canonical preset '${CANONICAL_PRESET}' build stamp present.\n`) + console.log(` ✓ Preset '${preset}' build stamp present in dist/circuits.\n`) + } + + /** + * Ensure `circuits/bin/` was last populated by `build:circuits` for the same preset. + * Without this, a secure benchmark could leave secure VKs in bin while `--check` diffs + * against committed insecure-512 `.sol` files. + */ + private assertCircuitsBinActivePreset(preset: string): void { + const activePath = join(this.circuitsDir, '.active-preset.json') + if (!existsSync(activePath)) { + throw new Error( + `Missing ${activePath} (which preset last built circuits/bin is unknown).\n` + + ` If dist/circuits/${preset}/ is already built, hydrate bin in seconds:\n` + + ` pnpm build:circuits --preset ${preset} --skip-if-built --no-clean --no-clean-targets\n` + + ` Otherwise run a full compile:\n` + + ` pnpm build:circuits --preset ${preset}`, + ) + } + let active: { preset?: string } = {} + try { + active = JSON.parse(readFileSync(activePath, 'utf-8')) + } catch (err: any) { + throw new Error(`Failed to parse ${activePath}: ${err.message}`) + } + if (active.preset !== preset) { + throw new Error( + `circuits/bin was last built for preset '${active.preset ?? '(missing)'}', but this run targets '${preset}'.\n` + + ` Fast fix (reuses dist/circuits/${preset}/, no full recompile):\n` + + ` pnpm build:circuits --preset ${preset} --skip-if-built --no-clean --no-clean-targets\n` + + ` Full compile only if dist is missing or stale:\n` + + ` pnpm build:circuits --preset ${preset}`, + ) + } + console.log(` ✓ circuits/bin active preset matches '${preset}'.\n`) } } @@ -560,6 +597,24 @@ async function main() { options.check = true } else if (arg === '--write') { options.check = false + } else if (arg === '--preset') { + const value = args[++i] + if (!value || value.startsWith('--')) { + console.error('Error: --preset requires a value (insecure-512 | secure-8192)') + process.exit(1) + } + if (!ALL_PRESETS.includes(value as (typeof ALL_PRESETS)[number])) { + console.error(`Error: unknown preset '${value}'. Expected one of: ${ALL_PRESETS.join(', ')}`) + process.exit(1) + } + options.preset = value + } else if (arg === '--output-dir') { + const value = args[++i] + if (!value || value.startsWith('--')) { + console.error('Error: --output-dir requires a path') + process.exit(1) + } + options.outputDir = value } else if (arg === '--group') { const value = args[++i] if (!value || value.startsWith('--')) { @@ -595,6 +650,10 @@ current circuit VKs is surfaced as a failure rather than a silent rewrite. Options: --check Verify committed verifiers match current VKs (no writes). Exits non-zero on drift. Used by test/benchmark/CI flows. + --preset BFV preset for circuits/bin (insecure-512 | secure-8192). + Defaults to insecure-512. With --check and a non-insecure preset, + only verifies dist/ + circuits/bin alignment (no .sol diff). + --output-dir Write generated verifiers here instead of the committed honk/ dir. --write Write/overwrite committed verifiers (this is the default when neither --check nor --write is passed). --circuits Circuit names (comma-separated). When omitted, generates all circuits. From 6d3fbb824ab591d4f1011f9040c5d0d94509ea6c Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sat, 23 May 2026 19:39:07 +0200 Subject: [PATCH 43/54] crisp & dockerfile --- crates/Dockerfile | 1 + examples/CRISP/Readme.md | 24 ++- examples/CRISP/client/.env.example | 4 +- examples/CRISP/crisp.dev.env.example | 17 ++ .../CRISP/docs/PROOF_AGGREGATION_AND_ZK.md | 180 ++++++++++++++++++ .../packages/crisp-contracts/.env.example | 3 + .../CRISP/packages/crisp-contracts/README.md | 28 +-- .../packages/crisp-contracts/deploy/deploy.ts | 40 +--- .../crisp-contracts/deploy/syncCrispEnv.ts | 133 +++++++++++++ examples/CRISP/scripts/crisp_deploy.sh | 26 ++- examples/CRISP/scripts/lib/dev_config.sh | 96 ++++++++++ examples/CRISP/scripts/setup.sh | 18 +- examples/CRISP/server/.env.example | 20 +- 13 files changed, 517 insertions(+), 73 deletions(-) create mode 100644 examples/CRISP/crisp.dev.env.example create mode 100644 examples/CRISP/docs/PROOF_AGGREGATION_AND_ZK.md create mode 100644 examples/CRISP/packages/crisp-contracts/deploy/syncCrispEnv.ts create mode 100644 examples/CRISP/scripts/lib/dev_config.sh diff --git a/crates/Dockerfile b/crates/Dockerfile index b651fcf9c..2724d7cd9 100644 --- a/crates/Dockerfile +++ b/crates/Dockerfile @@ -6,6 +6,7 @@ COPY packages/enclave-react ./packages/enclave-react COPY packages/enclave-sdk ./packages/enclave-sdk COPY packages/enclave-config ./packages/enclave-config COPY packages/enclave-contracts ./packages/enclave-contracts +COPY package.json . COPY pnpm-workspace.yaml . COPY pnpm-lock.yaml . diff --git a/examples/CRISP/Readme.md b/examples/CRISP/Readme.md index 22731193a..ef1aaa9e2 100644 --- a/examples/CRISP/Readme.md +++ b/examples/CRISP/Readme.md @@ -49,7 +49,11 @@ Before getting started, ensure you have installed: The simplest way to run CRISP is: ```bash -# Install dependencies and build everything +# Optional: choose local profile (copied to crisp.dev.env on first setup) +cp crisp.dev.env.example crisp.dev.env +# Edit CRISP_PROOF_AGGREGATION_ENABLED and CRISP_BFV_PRESET (see docs/PROOF_AGGREGATION_AND_ZK.md) + +# Install dependencies and build everything (applies crisp.dev.env → server/.env) pnpm dev:setup # Start all services (Hardhat, contracts, ciphernodes, program server, coordination server, and UI) @@ -169,7 +173,23 @@ program URL: The `pnpm dev:setup` command automatically creates `.env` files for the server and client from the `.env.example` templates (if they don't already exist). -The `enclave.config.yaml` file is automatically populated with contract addresses after deployment. +After `pnpm dev:up`, contract addresses are written automatically to `enclave.config.yaml`, +`server/.env`, and `client/.env` (no manual copy from `deployed_contracts.json`). + +### DKG proof aggregation and on-chain ZK + +Edit **`crisp.dev.env`** (created from `crisp.dev.env.example` on first `pnpm dev:setup`): + +| Variable | Default | Effect | +| --------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------- | +| `CRISP_BFV_PRESET` | `insecure-512` | DKG circuit build preset when aggregation is on | +| `CRISP_PROOF_AGGREGATION_ENABLED` | `false` | Synced to `server/.env`; controls DKG circuit build, deploy (`ENABLE_ZK_VERIFICATION`), and runtime aggregation | + +`pnpm dev:setup` applies this profile (build DKG circuits when needed, sync `server/.env`). +`pnpm dev:up` deploys contracts using the same flags. + +See **[docs/PROOF_AGGREGATION_AND_ZK.md](./docs/PROOF_AGGREGATION_AND_ZK.md)** for modes, address +sync, and troubleshooting (`VkHashMismatch`, etc.). ## Publishing packages to npm diff --git a/examples/CRISP/client/.env.example b/examples/CRISP/client/.env.example index d69bce5d2..4108162a6 100644 --- a/examples/CRISP/client/.env.example +++ b/examples/CRISP/client/.env.example @@ -1,7 +1,7 @@ VITE_ENCLAVE_API=http://127.0.0.1:4000 VITE_WALLETCONNECT_PROJECT_ID= -# Voting eligibility token (MockVotingToken). Set after CRISP deploy — see deploy output / server .env. +# Voting eligibility token (MockVotingToken). Updated automatically on `pnpm dev:up` deploy. # Enclave fee token (MockUSDC) is separate: packages/enclave-contracts/deployed_contracts.json → MockUSDC. -VITE_CRISP_TOKEN= +VITE_CRISP_TOKEN=0x5081a39b8A5f0E35a8D959395a630b68B74Dd30f # Addresses of requesters for which to show rounds in the UI. Comma separated Ethereum addresses. VITE_E3_REQUESTERS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266,0x70997970C51812dc3A010C7d01b50e0d17dc79C8 diff --git a/examples/CRISP/crisp.dev.env.example b/examples/CRISP/crisp.dev.env.example new file mode 100644 index 000000000..aa1923a39 --- /dev/null +++ b/examples/CRISP/crisp.dev.env.example @@ -0,0 +1,17 @@ +# CRISP local development profile (copied to crisp.dev.env on first `pnpm dev:setup`). +# Edit crisp.dev.env, then re-run `pnpm dev:setup` (and `pnpm dev:up` for a fresh deploy). +# +# See docs/PROOF_AGGREGATION_AND_ZK.md for details. + +# BFV parameter preset for DKG / on-chain Honk verifiers. +# Must match Enclave param_set used at requestE3 time (server uses param_set 0 today). +# insecure-512 -> param_set 0 (Micro committee, fast local dev) +# secure-8192 -> param_set 1 (production-grade; slower proving) +CRISP_BFV_PRESET=insecure-512 + +# When true: +# - dev:setup runs `pnpm build:circuits --preset ` +# - dev:up deploy sets ENABLE_ZK_VERIFICATION=true (BfvPkVerifier, fold attestations) +# - server/.env gets E3_PROOF_AGGREGATION_ENABLED=true +# When false (default): mock verifiers, no DKG circuit build, faster DKG. +CRISP_PROOF_AGGREGATION_ENABLED=false diff --git a/examples/CRISP/docs/PROOF_AGGREGATION_AND_ZK.md b/examples/CRISP/docs/PROOF_AGGREGATION_AND_ZK.md new file mode 100644 index 000000000..6eadd90e3 --- /dev/null +++ b/examples/CRISP/docs/PROOF_AGGREGATION_AND_ZK.md @@ -0,0 +1,180 @@ +# CRISP: proof aggregation and on-chain ZK verification + +## Configuration (`crisp.dev.env`) + +**Source of truth for local dev:** `examples/CRISP/crisp.dev.env` (from `crisp.dev.env.example`). + +| Variable | Default | Effect | +| --------------------------------- | -------------- | ----------------------------------------------------- | +| `CRISP_BFV_PRESET` | `insecure-512` | `pnpm build:circuits --preset` when aggregation is on | +| `CRISP_PROOF_AGGREGATION_ENABLED` | `false` | Drives setup, deploy, and `server/.env` | + +`pnpm dev:setup` copies the example if missing, syncs `E3_PROOF_AGGREGATION_ENABLED` into +`server/.env`, and builds DKG circuits only when aggregation is `true`. `pnpm dev:up` → +`crisp_deploy.sh` sets `ENABLE_ZK_VERIFICATION` from the same file. + +After changing `crisp.dev.env`, re-run `pnpm dev:setup` and a fresh `pnpm dev:up` (wipe +`.enclave/data` when switching modes). + +Lower-level switches (kept in sync by the scripts): + +| Switch | Where | Effect | +| ------------------------------ | ---------------------------------------------------- | ----------------------------- | +| `E3_PROOF_AGGREGATION_ENABLED` | `server/.env` (managed by setup) | Passed to `Enclave.requestE3` | +| `ENABLE_ZK_VERIFICATION` | Set at deploy from `CRISP_PROOF_AGGREGATION_ENABLED` | Real vs mock BFV verifiers | + +Misalignment causes `publishCommittee` to revert with **`VkHashMismatch()`** (`0x0c260259`). + +--- + +## Mode A — Local dev without proof aggregation (recommended) + +Use this for day-to-day CRISP development: faster DKG, no recursive proving, no on-chain BFV +verifier checks. + +### Configuration + +```bash +# crisp.dev.env +CRISP_BFV_PRESET=insecure-512 +CRISP_PROOF_AGGREGATION_ENABLED=false +``` + +### Steps + +```bash +# From examples/CRISP +pnpm dev:setup # once — skips DKG circuit build, syncs server/.env +pnpm dev:up +``` + +After deploy, ensure `server/.env` and `client/.env` match addresses printed by deploy or +`packages/crisp-contracts/deployed_contracts.json` → `localhost` (see +[Address sync](#address-sync-after-deploy)). + +```bash +pnpm cli init +``` + +### What you should see + +- Ciphernodes skip long `NodeDkgFold` / `zk_dkg_aggregation` runs +- `publishCommittee` succeeds without a DKG Honk proof (empty `proof` bytes are allowed when + aggregation is disabled on the E3) +- `POST /rounds/current` returns 200 once the indexer has recorded the round + +--- + +## Mode B — Full proof aggregation + on-chain ZK verification + +Use this to exercise the production DKG path: recursive folds, fold attestations, DKG aggregator +Honk proof, and `BfvPkVerifier` checks at `publishCommittee`. + +### Configuration + +```bash +# crisp.dev.env +CRISP_BFV_PRESET=insecure-512 +CRISP_PROOF_AGGREGATION_ENABLED=true +``` + +CRISP `requestE3` still uses on-chain `param_set = 0` (`InsecureThreshold512`) unless you change the +server — keep `CRISP_BFV_PRESET=insecure-512` for the default Micro committee. + +### Steps + +```bash +cd examples/CRISP +# Edit crisp.dev.env (or crisp.dev.env.example → crisp.dev.env) as above +pnpm dev:setup # builds DKG circuits + syncs server/.env +rm -rf .enclave/data # required when switching from Mode A +pnpm dev:up # deploy with ENABLE_ZK_VERIFICATION=true +pnpm cli init +``` + +`dev:setup` runs `pnpm build:circuits --preset ` before contract compile. `dev:up` +deploys via `crisp_deploy.sh` with `ENABLE_ZK_VERIFICATION=true`. + +**Do not** run `pnpm build:circuits` with a different preset after deploy without redeploying — that +causes **`VkHashMismatch()`** at `publishCommittee`. + +Expect DKG aggregation to take on the order of **minutes** per committee (fold + aggregator +proving). + +### What you should see + +- Logs: `loaded dkgFoldAttestationVerifier`, `NodeDkgFold complete`, `zk_dkg_aggregation`, then + `Publishing PublicKeyAggregated (dkg_evm_proof=present)` +- On-chain: `publishCommittee` succeeds (no `VkHashMismatch`) +- Registry / Enclave transition to key published; CRISP indexer can serve `/rounds/current` + +--- + +## Invalid combinations + +| Deploy | `E3_PROOF_AGGREGATION_ENABLED` | Result | +| ---------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Mock (`ENABLE_ZK_VERIFICATION` unset) | `true` | Ciphernodes generate real aggregation proofs, but `Enclave.pkVerifier` is `MockPkVerifier`. Attestation path may still run if a previous ZK deploy left `DkgFoldAttestationVerifier` on the registry from stale `deployed_contracts.json` on a **fresh** Anvil — always use `clean:deployments` + fresh chain. Prefer wiping `.enclave/data` and setting aggregation `false` for mock deploy. | +| ZK (`ENABLE_ZK_VERIFICATION=true`) | `false` | Valid but skips on-chain DKG proof verification; committee publication uses empty proof bytes. | +| ZK, circuits recompiled **after** deploy | `true` | **`VkHashMismatch()`** at `publishCommittee` — redeploy `BfvPkVerifier` (full ZK deploy) after `pnpm compile:circuits`. | + +--- + +## Address sync after deploy + +`pnpm dev:up` runs deploy then automatically updates: + +- `enclave.config.yaml` (ciphernode contract watches) +- `server/.env` (`ENCLAVE_ADDRESS`, `E3_PROGRAM_ADDRESS`, `CRISP_VOTING_TOKEN`, registry, fee token, + mock refs, `E3_PROOF_AGGREGATION_ENABLED` from `crisp.dev.env`) +- `client/.env` (`VITE_CRISP_TOKEN`) + +No manual copy from `deployed_contracts.json` is required. Stale addresses only happen if you skip +`dev:up` deploy and reuse an old Anvil state with new `.env` files. + +--- + +## Troubleshooting + +### `publishCommittee` reverts — `0x0c260259` (`VkHashMismatch`) + +**Cause:** `BfvPkVerifier` immutables (`expectedNodesFoldKeyHash`, `expectedC5KeyHash`) do not match +the VK hashes embedded in the DKG aggregator proof (usually circuits were rebuilt after verifier +deploy). + +**Fix:** + +1. Set `CRISP_PROOF_AGGREGATION_ENABLED=true` in `crisp.dev.env` (and matching `CRISP_BFV_PRESET`) +2. `pnpm dev:setup` then `rm -rf .enclave/data && pnpm dev:up` +3. `pnpm cli init` + +### `POST /rounds/current` → 500 + +Often a **symptom**, not the root cause: the CRISP indexer has no current round until on-chain DKG +progresses (e.g. committee key published). Fix DKG / `publishCommittee` first, then retry. If the +round was never created, run `pnpm cli init` after the server and ciphernodes are healthy. + +### `Historical events channel closed before all chains reported` + +Expected on localhost if Sepolia (`11155111`) is configured in ciphernode EVM sync but no Sepolia +RPC is running. Harmless for CRISP-on-Anvil. + +### After changing mode + +1. Fresh deploy (`clean:deployments` + deploy script for chosen mode) +2. Sync `.env` / `enclave.config.yaml` +3. `rm -rf .enclave/data` +4. Restart stack + `pnpm cli init` + +--- + +## Reference: what the scripts do + +| Step | Mode A (`CRISP_PROOF_AGGREGATION_ENABLED=false`) | Mode B (`=true`) | +| ---------------- | ----------------------------------------------------------------- | --------------------------------------------------------- | +| `pnpm dev:setup` | Skips `build:circuits`; sets aggregation `false` in `server/.env` | `pnpm build:circuits --preset …`; sets aggregation `true` | +| `pnpm dev:up` | Mock BFV verifiers | `ENABLE_ZK_VERIFICATION=true` + prints env vars | + +See also: `packages/enclave-contracts/scripts/deployEnclave.ts`, +`packages/enclave-contracts/contracts/verifiers/bfv/BfvPkVerifier.sol`, and +`agent/flow-trace/04_DKG_AND_COMPUTATION.md` for the full DKG publication flow. diff --git a/examples/CRISP/packages/crisp-contracts/.env.example b/examples/CRISP/packages/crisp-contracts/.env.example index 6279b65f7..1fd7fadc1 100644 --- a/examples/CRISP/packages/crisp-contracts/.env.example +++ b/examples/CRISP/packages/crisp-contracts/.env.example @@ -6,3 +6,6 @@ PRIVATE_KEY="" RPC_URL="" # Whether to use mock verifier and deploy a new mock token contract USE_MOCKS= + +# Prefer editing ../../crisp.dev.env — dev:setup / crisp_deploy.sh set ENABLE_ZK_VERIFICATION +# from CRISP_PROOF_AGGREGATION_ENABLED. See ../../docs/PROOF_AGGREGATION_AND_ZK.md. diff --git a/examples/CRISP/packages/crisp-contracts/README.md b/examples/CRISP/packages/crisp-contracts/README.md index 586866093..e32c4616f 100644 --- a/examples/CRISP/packages/crisp-contracts/README.md +++ b/examples/CRISP/packages/crisp-contracts/README.md @@ -22,30 +22,18 @@ pnpm test ## Deployment -### For testing +Local deploy is driven by **`../../crisp.dev.env`** (see +**[../../docs/PROOF_AGGREGATION_AND_ZK.md](../../docs/PROOF_AGGREGATION_AND_ZK.md)**): -For testing, you can deploy the contracts without using the Risc0 verifier. The following command -can be run: +- `pnpm dev:setup` — applies profile, builds DKG circuits when + `CRISP_PROOF_AGGREGATION_ENABLED=true` +- `pnpm dev:up` → `scripts/crisp_deploy.sh` — sets `ENABLE_ZK_VERIFICATION` from the same file -```bash -pnpm deploy:contracts:full:mock -``` - -This will also print out the environment variables needed for the CRISP server to work with your -newly deployed contracts. - -### Full deployment with Risc0Verifier - -You can deploy CRISP contracts only using: - -```bash -pnpm deploy:contracts -``` - -Or the following to deploy Enclave contracts too (useful for testing scenarios): +### CRISP-only deploy (Enclave already deployed) ```bash -pnpm deploy:contracts:full +pnpm deploy:contracts # production RISC0 verifier +pnpm deploy:contracts:full # also deploy Enclave stack (no ZK unless ENABLE_ZK_VERIFICATION=true) ``` ## CRISP Program diff --git a/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts b/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts index 893a7822e..9a1fd34e3 100644 --- a/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts +++ b/examples/CRISP/packages/crisp-contracts/deploy/deploy.ts @@ -3,8 +3,9 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -import { deployEnclave, readDeploymentArgs, updateE3Config } from '@enclave-e3/contracts/scripts' +import { deployEnclave, updateE3Config } from '@enclave-e3/contracts/scripts' import { deployCRISPContracts } from './crisp' +import { syncCrispEnvFromDeployments } from './syncCrispEnv' import path from 'path' import hre from 'hardhat' @@ -28,45 +29,20 @@ const __dirname = path.dirname(__filename) * Deploys the Enclave and CRISP contracts */ export const deploy = async () => { - const chain = hre.globalOptions.network + const chain = hre.globalOptions.network ?? 'localhost' const shouldDeployEnclave = Boolean(process.env.DEPLOY_ENCLAVE) - const shouldPrintEnv = Boolean(process.env.PRINT_ENV_VARS) + const withZkVerification = process.env.ENABLE_ZK_VERIFICATION === 'true' - // Mock BFV verifiers only: CRISP E2E uses E3_PROOF_AGGREGATION_ENABLED=false and does not - // ship compiled `*.vk_recursive_hash` artifacts required by BfvPkVerifier / BfvDecryptionVerifier. if (shouldDeployEnclave) { - await deployEnclave(true, false) + await deployEnclave(true, withZkVerification) } await deployCRISPContracts() - // this expects you to run it from CRISP's root - updateE3Config(chain, path.join(__dirname, '..', '..', '..', 'enclave.config.yaml'), contractMapping) + const enclaveConfigPath = path.join(__dirname, '..', '..', '..', 'enclave.config.yaml') + updateE3Config(chain, enclaveConfigPath, contractMapping) - if (shouldPrintEnv) { - const enclaveAddress = readDeploymentArgs('Enclave', chain)?.address - const feeTokenAddress = readDeploymentArgs('MockUSDC', chain)?.address - const programAddress = readDeploymentArgs('CRISPProgram', chain)?.address - const ciphernodeRegistryAddress = readDeploymentArgs('CiphernodeRegistryOwnable', chain)?.address - const votingTokenAddress = readDeploymentArgs('MockVotingToken', chain)?.address - - if (!enclaveAddress || !feeTokenAddress || !programAddress || !ciphernodeRegistryAddress || !votingTokenAddress) { - console.error('Error: Missing deployment addresses. Ensure all contracts are deployed.') - return - } - - console.log('\nAdd these to examples/CRISP/server/.env (and client/.env for VITE_CRISP_TOKEN):') - console.log( - [ - `ENCLAVE_ADDRESS=${enclaveAddress}`, - `FEE_TOKEN_ADDRESS=${feeTokenAddress}`, - `E3_PROGRAM_ADDRESS=${programAddress}`, - `CIPHERNODE_REGISTRY_ADDRESS=${ciphernodeRegistryAddress}`, - `CRISP_VOTING_TOKEN=${votingTokenAddress}`, - `VITE_CRISP_TOKEN=${votingTokenAddress}`, - ].join('\n'), - ) - } + syncCrispEnvFromDeployments(chain) } deploy().catch((err) => { diff --git a/examples/CRISP/packages/crisp-contracts/deploy/syncCrispEnv.ts b/examples/CRISP/packages/crisp-contracts/deploy/syncCrispEnv.ts new file mode 100644 index 000000000..57b2685e7 --- /dev/null +++ b/examples/CRISP/packages/crisp-contracts/deploy/syncCrispEnv.ts @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' + +import { readDeploymentArgs } from '@enclave-e3/contracts/scripts' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +/** examples/CRISP */ +const CRISP_ROOT = path.join(__dirname, '..', '..', '..') + +function parseSimpleEnvFile(filePath: string): Record { + if (!fs.existsSync(filePath)) { + return {} + } + const out: Record = {} + for (const line of fs.readFileSync(filePath, 'utf8').split('\n')) { + const trimmed = line.trim() + if (!trimmed || trimmed.startsWith('#')) { + continue + } + const eq = trimmed.indexOf('=') + if (eq === -1) { + continue + } + out[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim() + } + return out +} + +function ensureEnvFile(envPath: string, examplePath: string): void { + if (!fs.existsSync(envPath)) { + if (!fs.existsSync(examplePath)) { + throw new Error(`Missing ${examplePath}; cannot create ${envPath}`) + } + fs.copyFileSync(examplePath, envPath) + } +} + +/** Set or append KEY=value lines; preserves comments and unrelated keys. */ +function applyEnvUpdates(envPath: string, updates: Record): void { + let content = fs.readFileSync(envPath, 'utf8') + for (const [key, value] of Object.entries(updates)) { + const pattern = new RegExp(`^${key}=.*$`, 'm') + const line = `${key}=${value}` + if (pattern.test(content)) { + content = content.replace(pattern, line) + } else { + if (!content.endsWith('\n')) { + content += '\n' + } + content += `${line}\n` + } + } + fs.writeFileSync(envPath, content) +} + +function deploymentAddress(contractName: string, chain: string): string | undefined { + return readDeploymentArgs(contractName, chain)?.address +} + +/** + * Writes localhost deployment addresses into server/.env and client/.env, and + * syncs E3_PROOF_AGGREGATION_ENABLED from crisp.dev.env. + */ +export function syncCrispEnvFromDeployments(chain: string): void { + const enclaveAddress = deploymentAddress('Enclave', chain) + const feeTokenAddress = deploymentAddress('MockUSDC', chain) + const programAddress = deploymentAddress('CRISPProgram', chain) + const registryAddress = deploymentAddress('CiphernodeRegistryOwnable', chain) + const votingTokenAddress = deploymentAddress('MockVotingToken', chain) + + const missing: string[] = [] + if (!enclaveAddress) missing.push('Enclave') + if (!feeTokenAddress) missing.push('MockUSDC') + if (!programAddress) missing.push('CRISPProgram') + if (!registryAddress) missing.push('CiphernodeRegistryOwnable') + if (!votingTokenAddress) missing.push('MockVotingToken') + + if (missing.length > 0) { + throw new Error(`Cannot sync CRISP .env files: missing deployments for ${missing.join(', ')} on chain "${chain}"`) + } + + const crispDev = parseSimpleEnvFile(path.join(CRISP_ROOT, 'crisp.dev.env')) + const proofAggregation = + crispDev.CRISP_PROOF_AGGREGATION_ENABLED ?? + parseSimpleEnvFile(path.join(CRISP_ROOT, 'crisp.dev.env.example')).CRISP_PROOF_AGGREGATION_ENABLED ?? + 'false' + + const serverEnv = path.join(CRISP_ROOT, 'server', '.env') + const clientEnv = path.join(CRISP_ROOT, 'client', '.env') + + ensureEnvFile(serverEnv, path.join(CRISP_ROOT, 'server', '.env.example')) + ensureEnvFile(clientEnv, path.join(CRISP_ROOT, 'client', '.env.example')) + + const serverUpdates: Record = { + ENCLAVE_ADDRESS: enclaveAddress!, + FEE_TOKEN_ADDRESS: feeTokenAddress!, + E3_PROGRAM_ADDRESS: programAddress!, + CIPHERNODE_REGISTRY_ADDRESS: registryAddress!, + CRISP_VOTING_TOKEN: votingTokenAddress!, + E3_PROOF_AGGREGATION_ENABLED: proofAggregation, + } + + const mockMappings: Array<[string, string]> = [ + ['MOCK_COMPUTE_PROVIDER_ADDRESS', 'MockComputeProvider'], + ['MOCK_DECRYPTION_VERIFIER_ADDRESS', 'MockDecryptionVerifier'], + ['MOCK_PK_VERIFIER_ADDRESS', 'MockPkVerifier'], + ['MOCK_E3_PROGRAM_ADDRESS', 'MockE3Program'], + ] + for (const [envKey, contractName] of mockMappings) { + const addr = deploymentAddress(contractName, chain) + if (addr) { + serverUpdates[envKey] = addr + } + } + + applyEnvUpdates(serverEnv, serverUpdates) + applyEnvUpdates(clientEnv, { + VITE_CRISP_TOKEN: votingTokenAddress!, + }) + + console.log(`Synced deployment addresses → ${path.relative(CRISP_ROOT, serverEnv)}`) + console.log(`Synced VITE_CRISP_TOKEN → ${path.relative(CRISP_ROOT, clientEnv)}`) + console.log(` E3_PROOF_AGGREGATION_ENABLED=${proofAggregation} (from crisp.dev.env)`) +} diff --git a/examples/CRISP/scripts/crisp_deploy.sh b/examples/CRISP/scripts/crisp_deploy.sh index 402248406..72474c41b 100755 --- a/examples/CRISP/scripts/crisp_deploy.sh +++ b/examples/CRISP/scripts/crisp_deploy.sh @@ -2,11 +2,31 @@ set -euo pipefail -echo "Deploying CRISP Contracts..." +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck disable=SC1091 +source "${SCRIPT_DIR}/lib/dev_config.sh" + +load_crisp_dev_config +print_crisp_dev_config_summary + +echo "Deploying CRISP contracts..." export PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +export USE_MOCKS=true +export DEPLOY_ENCLAVE=true -cd packages/crisp-contracts +cd "${CRISP_ROOT}/packages/crisp-contracts" pnpm clean:deployments --network localhost -USE_MOCKS=true pnpm deploy:contracts:full:mock --network localhost + +if [[ "$CRISP_PROOF_AGGREGATION_ENABLED" == "true" ]]; then + export ENABLE_ZK_VERIFICATION=true + echo "Deploy: ENABLE_ZK_VERIFICATION=true (BfvPkVerifier + fold attestation)" +else + unset ENABLE_ZK_VERIFICATION + echo "Deploy: mock BFV verifiers (ENABLE_ZK_VERIFICATION unset)" +fi + +pnpm deploy:contracts --network localhost + +apply_crisp_dev_config_to_server_env diff --git a/examples/CRISP/scripts/lib/dev_config.sh b/examples/CRISP/scripts/lib/dev_config.sh new file mode 100644 index 000000000..0ad25b4b8 --- /dev/null +++ b/examples/CRISP/scripts/lib/dev_config.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# Shared CRISP local dev configuration. Source from setup.sh / crisp_deploy.sh. + +_crisp_dev_config_root() { + (cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +} + +load_crisp_dev_config() { + CRISP_ROOT="$(_crisp_dev_config_root)" + REPO_ROOT="$(cd "${CRISP_ROOT}/../.." && pwd)" + + local cfg="${CRISP_ROOT}/crisp.dev.env" + if [[ ! -f "$cfg" ]]; then + cp "${CRISP_ROOT}/crisp.dev.env.example" "$cfg" + echo "Created ${cfg} from crisp.dev.env.example" + fi + + set -a + # shellcheck disable=SC1090 + source "$cfg" + set +a + + CRISP_BFV_PRESET="${CRISP_BFV_PRESET:-insecure-512}" + CRISP_PROOF_AGGREGATION_ENABLED="${CRISP_PROOF_AGGREGATION_ENABLED:-false}" + + case "$CRISP_BFV_PRESET" in + insecure-512 | secure-8192) ;; + *) + echo "Invalid CRISP_BFV_PRESET='${CRISP_BFV_PRESET}' (use insecure-512 or secure-8192)" >&2 + exit 1 + ;; + esac + + case "$CRISP_PROOF_AGGREGATION_ENABLED" in + true | false) ;; + *) + echo "Invalid CRISP_PROOF_AGGREGATION_ENABLED='${CRISP_PROOF_AGGREGATION_ENABLED}' (use true or false)" >&2 + exit 1 + ;; + esac + + if [[ "$CRISP_PROOF_AGGREGATION_ENABLED" == "true" ]]; then + export ENABLE_ZK_VERIFICATION=true + else + unset ENABLE_ZK_VERIFICATION + fi + + export CRISP_BFV_PRESET CRISP_PROOF_AGGREGATION_ENABLED CRISP_ROOT REPO_ROOT +} + +_set_env_kv() { + local file=$1 key=$2 value=$3 + if [[ -f "$file" ]] && grep -q "^${key}=" "$file"; then + local tmp + tmp="$(mktemp)" + sed "s|^${key}=.*|${key}=${value}|" "$file" >"$tmp" + mv "$tmp" "$file" + else + echo "${key}=${value}" >>"$file" + fi +} + +apply_crisp_dev_config_to_server_env() { + local server_env="${CRISP_ROOT}/server/.env" + if [[ ! -f "$server_env" ]]; then + cp "${CRISP_ROOT}/server/.env.example" "$server_env" + fi + _set_env_kv "$server_env" "E3_PROOF_AGGREGATION_ENABLED" "$CRISP_PROOF_AGGREGATION_ENABLED" +} + +compile_enclave_dkg_circuits_if_needed() { + if [[ "$CRISP_PROOF_AGGREGATION_ENABLED" != "true" ]]; then + echo "Skipping enclave DKG circuit build (CRISP_PROOF_AGGREGATION_ENABLED=false in crisp.dev.env)" + return 0 + fi + + echo "Building enclave DKG circuits (preset=${CRISP_BFV_PRESET})..." + ( + cd "${REPO_ROOT}" + pnpm build:circuits --preset "${CRISP_BFV_PRESET}" --skip-if-built + ) +} + +print_crisp_dev_config_summary() { + cat < Date: Sat, 23 May 2026 19:50:33 +0200 Subject: [PATCH 44/54] fix vercel action --- examples/CRISP/Readme.md | 13 + examples/CRISP/client/.npmrc | 2 + examples/CRISP/client/pnpm-lock.yaml | 11004 ++++++++++++++++ .../client/src/providers/Web3Provider.tsx | 1 - examples/CRISP/client/src/utils/methods.ts | 4 +- examples/CRISP/client/vercel.json | 6 +- 6 files changed, 11026 insertions(+), 4 deletions(-) create mode 100644 examples/CRISP/client/.npmrc create mode 100644 examples/CRISP/client/pnpm-lock.yaml diff --git a/examples/CRISP/Readme.md b/examples/CRISP/Readme.md index ef1aaa9e2..265dbf45c 100644 --- a/examples/CRISP/Readme.md +++ b/examples/CRISP/Readme.md @@ -191,6 +191,19 @@ Edit **`crisp.dev.env`** (created from `crisp.dev.env.example` on first `pnpm de See **[docs/PROOF_AGGREGATION_AND_ZK.md](./docs/PROOF_AGGREGATION_AND_ZK.md)** for modes, address sync, and troubleshooting (`VkHashMismatch`, etc.). +### Vercel (CRISP client) + +Deploy from **`examples/CRISP/client`**. The build uses the published **`@crisp-e3/sdk@0.9.0`** on +npm (`pnpm install --ignore-workspace`), not the monorepo workspace — so it does not compile Noir +circuits on Vercel. + +- **Project root directory:** `examples/CRISP/client` +- **`vercel build` in CI:** run from the **repository root** (not `cd examples/CRISP/client` first) +- Optional Vercel env: `ENABLE_EXPERIMENTAL_COREPACK=1` + +Commit `examples/CRISP/client/pnpm-lock.yaml` after dependency bumps +(`pnpm install --ignore-workspace` in that directory) for reproducible installs. + ## Publishing packages to npm In order to publish a new version of the CRISP packages to npm, you can use: diff --git a/examples/CRISP/client/.npmrc b/examples/CRISP/client/.npmrc new file mode 100644 index 000000000..6906473d7 --- /dev/null +++ b/examples/CRISP/client/.npmrc @@ -0,0 +1,2 @@ +# Vercel / standalone installs: resolve @crisp-e3/sdk from npm (0.9.0), not the monorepo workspace. +ignore-workspace=true diff --git a/examples/CRISP/client/pnpm-lock.yaml b/examples/CRISP/client/pnpm-lock.yaml new file mode 100644 index 000000000..c43765d01 --- /dev/null +++ b/examples/CRISP/client/pnpm-lock.yaml @@ -0,0 +1,11004 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@crisp-e3/sdk': + specifier: 0.9.0 + version: 0.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@emotion/babel-plugin': + specifier: ^11.11.0 + version: 11.13.5 + '@emotion/react': + specifier: ^11.11.4 + version: 11.14.0(@types/react@18.3.29)(react@18.3.1) + '@phosphor-icons/react': + specifier: ^2.1.4 + version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@svgr/rollup': + specifier: ^8.1.0 + version: 8.1.0(rollup@4.60.4)(typescript@5.9.3) + '@tanstack/react-query': + specifier: ^5.74.3 + version: 5.100.14(react@18.3.1) + axios: + specifier: 1.13.2 + version: 1.13.2 + connectkit: + specifier: ^1.9.0 + version: 1.9.2(@babel/core@7.29.0)(@tanstack/react-query@5.100.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + ethers: + specifier: ^6.12.0 + version: 6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + react-markdown: + specifier: ^9.0.1 + version: 9.1.0(@types/react@18.3.29)(react@18.3.1) + react-router-dom: + specifier: ^6.22.3 + version: 6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-syntax-highlighter: + specifier: ^15.5.0 + version: 15.6.6(react@18.3.1) + viem: + specifier: 2.38.6 + version: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + vite-plugin-node-polyfills: + specifier: ^0.22.0 + version: 0.22.0(rollup@4.60.4)(vite@5.4.21(@types/node@22.7.5)) + vite-plugin-top-level-await: + specifier: ^1.4.1 + version: 1.6.0(rollup@4.60.4)(vite@5.4.21(@types/node@22.7.5)) + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.9.3)(vite@5.4.21(@types/node@22.7.5)) + wagmi: + specifier: ^2.14.16 + version: 2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + devDependencies: + '@tailwindcss/typography': + specifier: ^0.5.12 + version: 0.5.19(tailwindcss@3.4.19) + '@types/react': + specifier: ^18.2.66 + version: 18.3.29 + '@types/react-dom': + specifier: ^18.2.22 + version: 18.3.7(@types/react@18.3.29) + '@types/react-syntax-highlighter': + specifier: ^15.5.11 + version: 15.5.13 + '@vitejs/plugin-react': + specifier: ^4.2.1 + version: 4.7.0(vite@5.4.21(@types/node@22.7.5)) + add: + specifier: ^2.0.6 + version: 2.0.6 + autoprefixer: + specifier: ^10.4.19 + version: 10.5.0(postcss@8.5.15) + gh-pages: + specifier: ^6.1.1 + version: 6.3.0 + prettier: + specifier: ^3.2.5 + version: 3.8.3 + prettier-plugin-tailwindcss: + specifier: ^0.5.13 + version: 0.5.14(prettier@3.8.3) + tailwindcss: + specifier: ^3.4.2 + version: 3.4.19 + typescript: + specifier: ^5.8.3 + version: 5.9.3 + vite: + specifier: ^5.2.0 + version: 5.4.21(@types/node@22.7.5) + +packages: + + '@aave/account@0.2.0': + resolution: {integrity: sha512-fk9KcC0He0WNvUGytxVv4urh8F+zznm6ZdpgKnNvq9jKUpnc2HSSn7mS3n4h13h80WtF8OL6rpL50ctqqc4/Nw==} + peerDependencies: + react: 17.x || 18.x || 19.x + react-dom: 17.x || 18.x || 19.x + viem: 2.x + wagmi: 2.x + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + viem: + optional: true + wagmi: + optional: true + + '@adraffy/ens-normalize@1.10.1': + resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@aztec/bb.js@3.0.0-nightly.20260102': + resolution: {integrity: sha512-2SkTJw5sNyXU301OVOHOuqhd5JA3D+HE4cvKxj/hDG7rBiOi851e6swL29l2Y/UpcW2eNZMmmmk/NZgiDfDAgg==} + hasBin: true + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.3': + resolution: {integrity: sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3': + resolution: {integrity: sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': + resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.28.6': + resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.6': + resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.28.6': + resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6': + resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.4': + resolution: {integrity: sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.28.6': + resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.6': + resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.28.6': + resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.28.6': + resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-constant-elements@7.27.1': + resolution: {integrity: sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.28.0': + resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.28.6': + resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.27.1': + resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.28.6': + resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.6': + resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.28.6': + resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6': + resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.5': + resolution: {integrity: sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-react@7.28.5': + resolution: {integrity: sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@base-org/account@2.4.0': + resolution: {integrity: sha512-A4Umpi8B9/pqR78D1Yoze4xHyQaujioVRqqO3d6xuDFw9VRtjg6tK3bPlwE0aW+nVH/ntllCpPa2PbI8Rnjcug==} + + '@coinbase/cdp-sdk@1.50.0': + resolution: {integrity: sha512-lKK6aC2z8q8C3IA39unNuWc8lgM0hU9mSqkdd7Bncf5xvT28f8G6upexFtJweNwxkeAJwiLSgBkwOhqMK2/OGQ==} + + '@coinbase/wallet-sdk@3.9.3': + resolution: {integrity: sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw==} + + '@coinbase/wallet-sdk@4.3.6': + resolution: {integrity: sha512-4q8BNG1ViL4mSAAvPAtpwlOs1gpC+67eQtgIwNvT3xyeyFFd+guwkc8bcX5rTmQhXpqnhzC4f0obACbP9CqMSA==} + + '@crisp-e3/sdk@0.9.0': + resolution: {integrity: sha512-LpiJrks9ypj0CKrsHMoYh+TC1Xcm3dspYDWLBbnnMxZUUoFDbLO6ckgVUNh4vPQi5MwGGhTUU6X4M3lTNja92w==} + + '@crisp-e3/zk-inputs@0.9.0': + resolution: {integrity: sha512-c7/IYPRvr0/M68wfQDyNotcC71VAy+CW07KmmU8s4Lau+nHEa/jb2p6VVfQymctZ/nh3j3DzzD2yDR1OE5gfjg==} + + '@ecies/ciphers@0.2.6': + resolution: {integrity: sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==} + engines: {bun: '>=1', deno: '>=2.7.10', node: '>=16'} + peerDependencies: + '@noble/ciphers': ^1.0.0 + + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@0.8.8': + resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} + + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.7.4': + resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/stylis@0.8.5': + resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==} + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@ethereumjs/common@3.2.0': + resolution: {integrity: sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==} + + '@ethereumjs/rlp@4.0.1': + resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==} + engines: {node: '>=14'} + hasBin: true + + '@ethereumjs/tx@4.2.0': + resolution: {integrity: sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==} + engines: {node: '>=14'} + + '@ethereumjs/util@8.1.0': + resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==} + engines: {node: '>=14'} + + '@gemini-wallet/core@0.3.2': + resolution: {integrity: sha512-Z4aHi3ECFf5oWYWM3F1rW83GJfB9OvhBYPTmb5q+VyK3uvzvS48lwo+jwh2eOoCRWEuT/crpb9Vwp2QaS5JqgQ==} + peerDependencies: + viem: '>=2.0.0' + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@lit-labs/ssr-dom-shim@1.6.0': + resolution: {integrity: sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==} + + '@lit/reactive-element@2.1.2': + resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} + + '@metamask/eth-json-rpc-provider@1.0.1': + resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==} + engines: {node: '>=14.0.0'} + + '@metamask/json-rpc-engine@7.3.3': + resolution: {integrity: sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==} + engines: {node: '>=16.0.0'} + + '@metamask/json-rpc-engine@8.0.2': + resolution: {integrity: sha512-IoQPmql8q7ABLruW7i4EYVHWUbF74yrp63bRuXV5Zf9BQwcn5H9Ww1eLtROYvI1bUXwOiHZ6qT5CWTrDc/t/AA==} + engines: {node: '>=16.0.0'} + + '@metamask/json-rpc-middleware-stream@7.0.2': + resolution: {integrity: sha512-yUdzsJK04Ev98Ck4D7lmRNQ8FPioXYhEUZOMS01LXW8qTvPGiRVXmVltj2p4wrLkh0vW7u6nv0mNl5xzC5Qmfg==} + engines: {node: '>=16.0.0'} + + '@metamask/object-multiplex@2.1.0': + resolution: {integrity: sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA==} + engines: {node: ^16.20 || ^18.16 || >=20} + + '@metamask/onboarding@1.0.1': + resolution: {integrity: sha512-FqHhAsCI+Vacx2qa5mAFcWNSrTcVGMNjzxVgaX8ECSny/BJ9/vgXP9V7WF/8vb9DltPeQkxr+Fnfmm6GHfmdTQ==} + + '@metamask/providers@16.1.0': + resolution: {integrity: sha512-znVCvux30+3SaUwcUGaSf+pUckzT5ukPRpcBmy+muBLC0yaWnBcvDqGfcsw6CBIenUdFrVoAFa8B6jsuCY/a+g==} + engines: {node: ^18.18 || >=20} + + '@metamask/rpc-errors@6.4.0': + resolution: {integrity: sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==} + engines: {node: '>=16.0.0'} + + '@metamask/rpc-errors@7.0.2': + resolution: {integrity: sha512-YYYHsVYd46XwY2QZzpGeU4PSdRhHdxnzkB8piWGvJW2xbikZ3R+epAYEL4q/K8bh9JPTucsUdwRFnACor1aOYw==} + engines: {node: ^18.20 || ^20.17 || >=22} + + '@metamask/safe-event-emitter@2.0.0': + resolution: {integrity: sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==} + + '@metamask/safe-event-emitter@3.1.2': + resolution: {integrity: sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA==} + engines: {node: '>=12.0.0'} + + '@metamask/sdk-analytics@0.0.5': + resolution: {integrity: sha512-fDah+keS1RjSUlC8GmYXvx6Y26s3Ax1U9hGpWb6GSY5SAdmTSIqp2CvYy6yW0WgLhnYhW+6xERuD0eVqV63QIQ==} + + '@metamask/sdk-communication-layer@0.33.1': + resolution: {integrity: sha512-0bI9hkysxcfbZ/lk0T2+aKVo1j0ynQVTuB3sJ5ssPWlz+Z3VwveCkP1O7EVu1tsVVCb0YV5WxK9zmURu2FIiaA==} + peerDependencies: + cross-fetch: ^4.0.0 + eciesjs: '*' + eventemitter2: ^6.4.9 + readable-stream: ^3.6.2 + socket.io-client: ^4.5.1 + + '@metamask/sdk-install-modal-web@0.32.1': + resolution: {integrity: sha512-MGmAo6qSjf1tuYXhCu2EZLftq+DSt5Z7fsIKr2P+lDgdTPWgLfZB1tJKzNcwKKOdf6q9Qmmxn7lJuI/gq5LrKw==} + + '@metamask/sdk@0.33.1': + resolution: {integrity: sha512-1mcOQVGr9rSrVcbKPNVzbZ8eCl1K0FATsYH3WJ/MH4WcZDWGECWrXJPNMZoEAkLxWiMe8jOQBumg2pmcDa9zpQ==} + + '@metamask/superstruct@3.2.1': + resolution: {integrity: sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==} + engines: {node: '>=16.0.0'} + + '@metamask/utils@11.11.0': + resolution: {integrity: sha512-0nF2CWjWQr/m0Y2t2lJnBTU1/CZPPTvKvcESLplyWe/tyeb8zFOi/FeneDmaFnML6LYRIGZU6f+xR0jKAIUZfw==} + engines: {node: ^18.18 || ^20.14 || >=22} + + '@metamask/utils@5.0.2': + resolution: {integrity: sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g==} + engines: {node: '>=14.0.0'} + + '@metamask/utils@8.5.0': + resolution: {integrity: sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==} + engines: {node: '>=16.0.0'} + + '@metamask/utils@9.3.0': + resolution: {integrity: sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==} + engines: {node: '>=16.0.0'} + + '@motionone/animation@10.18.0': + resolution: {integrity: sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==} + + '@motionone/dom@10.12.0': + resolution: {integrity: sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==} + + '@motionone/easing@10.18.0': + resolution: {integrity: sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==} + + '@motionone/generators@10.18.0': + resolution: {integrity: sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==} + + '@motionone/types@10.17.1': + resolution: {integrity: sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==} + + '@motionone/utils@10.18.0': + resolution: {integrity: sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@noble/ciphers@1.2.1': + resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + + '@noble/curves@1.4.2': + resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} + + '@noble/curves@1.8.0': + resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.8.1': + resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + + '@noble/hashes@1.7.0': + resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@noir-lang/acvm_js@1.0.0-beta.16': + resolution: {integrity: sha512-2xcEE0mCFaZEvVoKVVsG5DTUgj68duFosIvV0Z19Gn5YM1Ay5pHQnFBX4xfhJPA/0Ex8yMRZA/QDE4jpMz96KQ==} + + '@noir-lang/noir_js@1.0.0-beta.16': + resolution: {integrity: sha512-DHgD73WVR6ly6c+5ocrLWLooppYwTsvx4pyndloXlXm8LcJ4ouySg4dNgN8hqNLrTixqxjWu8wBy7eYiAS1iHQ==} + + '@noir-lang/noirc_abi@1.0.0-beta.16': + resolution: {integrity: sha512-4RpTEyw34ap65B7gQQLTeXoRtFACsATd+o4fgjLgvoIBiwwftcp2+MOFGJA3icRUJyBXknmlNu+HXCOI7Vl+Xg==} + + '@noir-lang/types@1.0.0-beta.16': + resolution: {integrity: sha512-QDZRdvC3NZ1USCOkmcgONoerQl5eWpl2uPreE/BN+bidgz+Q3cANW/KO9tt0ZeWWVMfLrYV2n7o6l0y8pJUV1g==} + + '@paulmillr/qr@0.2.1': + resolution: {integrity: sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==} + deprecated: 'The package is now available as "qr": npm install qr' + + '@phosphor-icons/react@2.1.10': + resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8' + react-dom: '>= 16.8' + + '@remix-run/router@1.23.2': + resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==} + engines: {node: '>=14.0.0'} + + '@reown/appkit-common@1.7.8': + resolution: {integrity: sha512-ridIhc/x6JOp7KbDdwGKY4zwf8/iK8EYBl+HtWrruutSLwZyVi5P8WaZa+8iajL6LcDcDF7LoyLwMTym7SRuwQ==} + + '@reown/appkit-controllers@1.7.8': + resolution: {integrity: sha512-IdXlJlivrlj6m63VsGLsjtPHHsTWvKGVzWIP1fXZHVqmK+rZCBDjCi9j267Rb9/nYRGHWBtlFQhO8dK35WfeDA==} + + '@reown/appkit-pay@1.7.8': + resolution: {integrity: sha512-OSGQ+QJkXx0FEEjlpQqIhT8zGJKOoHzVnyy/0QFrl3WrQTjCzg0L6+i91Ad5Iy1zb6V5JjqtfIFpRVRWN4M3pw==} + + '@reown/appkit-polyfills@1.7.8': + resolution: {integrity: sha512-W/kq786dcHHAuJ3IV2prRLEgD/2iOey4ueMHf1sIFjhhCGMynMkhsOhQMUH0tzodPqUgAC494z4bpIDYjwWXaA==} + + '@reown/appkit-scaffold-ui@1.7.8': + resolution: {integrity: sha512-RCeHhAwOrIgcvHwYlNWMcIDibdI91waaoEYBGw71inE0kDB8uZbE7tE6DAXJmDkvl0qPh+DqlC4QbJLF1FVYdQ==} + + '@reown/appkit-ui@1.7.8': + resolution: {integrity: sha512-1hjCKjf6FLMFzrulhl0Y9Vb9Fu4royE+SXCPSWh4VhZhWqlzUFc7kutnZKx8XZFVQH4pbBvY62SpRC93gqoHow==} + + '@reown/appkit-utils@1.7.8': + resolution: {integrity: sha512-8X7UvmE8GiaoitCwNoB86pttHgQtzy4ryHZM9kQpvjQ0ULpiER44t1qpVLXNM4X35O0v18W0Dk60DnYRMH2WRw==} + peerDependencies: + valtio: 1.13.2 + + '@reown/appkit-wallet@1.7.8': + resolution: {integrity: sha512-kspz32EwHIOT/eg/ZQbFPxgXq0B/olDOj3YMu7gvLEFz4xyOFd/wgzxxAXkp5LbG4Cp++s/elh79rVNmVFdB9A==} + + '@reown/appkit@1.7.8': + resolution: {integrity: sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/plugin-inject@5.0.5': + resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-virtual@3.0.2': + resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} + cpu: [x64] + os: [win32] + + '@safe-global/safe-apps-provider@0.18.6': + resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==} + + '@safe-global/safe-apps-sdk@9.1.0': + resolution: {integrity: sha512-N5p/ulfnnA2Pi2M3YeWjULeWbjo7ei22JwU/IXnhoHzKq3pYCN6ynL9mJBOlvDVv892EgLPCWCOwQk/uBT2v0Q==} + + '@safe-global/safe-gateway-typescript-sdk@3.23.1': + resolution: {integrity: sha512-6ORQfwtEJYpalCeVO21L4XXGSdbEMfyp2hEv6cP82afKXSwvse6d3sdelgaPWUxHIsFRkWvHDdzh8IyyKHZKxw==} + engines: {node: '>=16'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + '@scure/base@1.1.9': + resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} + + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/bip32@1.4.0': + resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} + + '@scure/bip32@1.6.2': + resolution: {integrity: sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.3.0': + resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} + + '@scure/bip39@1.5.4': + resolution: {integrity: sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@solana-program/system@0.10.0': + resolution: {integrity: sha512-Go+LOEZmqmNlfr+Gjy5ZWAdY5HbYzk2RBewD9QinEU/bBSzpFfzqDRT55JjFRBGJUvMgf3C2vfXEGT4i8DSI4g==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana-program/token@0.9.0': + resolution: {integrity: sha512-vnZxndd4ED4Fc56sw93cWZ2djEeeOFxtaPS8SPf5+a+JZjKA/EnKqzbE1y04FuMhIVrLERQ8uR8H2h72eZzlsA==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana/accounts@5.5.1': + resolution: {integrity: sha512-TfOY9xixg5rizABuLVuZ9XI2x2tmWUC/OoN556xwfDlhBHBjKfszicYYOyD6nbFmwTGYarCmyGIdteXxTXIdhQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/addresses@5.5.1': + resolution: {integrity: sha512-5xoah3Q9G30HQghu/9BiHLb5pzlPKRC3zydQDmE3O9H//WfayxTFppsUDCL6FjYUHqj/wzK6CWHySglc2RkpdA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/assertions@5.5.1': + resolution: {integrity: sha512-YTCSWAlGwSlVPnWtWLm3ukz81wH4j2YaCveK+TjpvUU88hTy6fmUqxi0+hvAMAe4zKXpJyj3Az7BrLJRxbIm4Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-core@5.5.1': + resolution: {integrity: sha512-TgBt//bbKBct0t6/MpA8ElaOA3sa8eYVvR7LGslCZ84WiAwwjCY0lW/lOYsFHJQzwREMdUyuEyy5YWBKtdh8Rw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-data-structures@5.5.1': + resolution: {integrity: sha512-97bJWGyUY9WvBz3mX1UV3YPWGDTez6btCfD0ip3UVEXJbItVuUiOkzcO5iFDUtQT5riKT6xC+Mzl+0nO76gd0w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-numbers@5.5.1': + resolution: {integrity: sha512-rllMIZAHqmtvC0HO/dc/21wDuWaD0B8Ryv8o+YtsICQBuiL/0U4AGwH7Pi5GNFySYk0/crSuwfIqQFtmxNSPFw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-strings@5.5.1': + resolution: {integrity: sha512-7klX4AhfHYA+uKKC/nxRGP2MntbYQCR3N6+v7bk1W/rSxYuhNmt+FN8aoThSZtWIKwN6BEyR1167ka8Co1+E7A==} + engines: {node: '>=20.18.0'} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: ^5.0.0 + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true + + '@solana/codecs@5.5.1': + resolution: {integrity: sha512-Vea29nJub/bXjfzEV7ZZQ/PWr1pYLZo3z0qW0LQL37uKKVzVFRQlwetd7INk3YtTD3xm9WUYr7bCvYUk3uKy2g==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/errors@5.5.1': + resolution: {integrity: sha512-vFO3p+S7HoyyrcAectnXbdsMfwUzY2zYFUc2DEe5BwpiE9J1IAxPBGjOWO6hL1bbYdBrlmjNx8DXCslqS+Kcmg==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/fast-stable-stringify@5.5.1': + resolution: {integrity: sha512-Ni7s2FN33zTzhTFgRjEbOVFO+UAmK8qi3Iu0/GRFYK4jN696OjKHnboSQH/EacQ+yGqS54bfxf409wU5dsLLCw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/functional@5.5.1': + resolution: {integrity: sha512-tTHoJcEQq3gQx5qsdsDJ0LEJeFzwNpXD80xApW9o/PPoCNimI3SALkZl+zNW8VnxRrV3l3yYvfHWBKe/X3WG3w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instruction-plans@5.5.1': + resolution: {integrity: sha512-7z3CB7YMcFKuVvgcnNY8bY6IsZ8LG61Iytbz7HpNVGX2u1RthOs1tRW8luTzSG1MPL0Ox7afyAVMYeFqSPHnaQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/instructions@5.5.1': + resolution: {integrity: sha512-h0G1CG6S+gUUSt0eo6rOtsaXRBwCq1+Js2a+Ps9Bzk9q7YHNFA75/X0NWugWLgC92waRp66hrjMTiYYnLBoWOQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/keys@5.5.1': + resolution: {integrity: sha512-KRD61cL7CRL+b4r/eB9dEoVxIf/2EJ1Pm1DmRYhtSUAJD2dJ5Xw8QFuehobOGm9URqQ7gaQl+Fkc1qvDlsWqKg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/kit@5.5.1': + resolution: {integrity: sha512-irKUGiV2yRoyf+4eGQ/ZeCRxa43yjFEL1DUI5B0DkcfZw3cr0VJtVJnrG8OtVF01vT0OUfYOcUn6zJW5TROHvQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/nominal-types@5.5.1': + resolution: {integrity: sha512-I1ImR+kfrLFxN5z22UDiTWLdRZeKtU0J/pkWkO8qm/8WxveiwdIv4hooi8pb6JnlR4mSrWhq0pCIOxDYrL9GIQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/offchain-messages@5.5.1': + resolution: {integrity: sha512-g+xHH95prTU+KujtbOzj8wn+C7ZNoiLhf3hj6nYq3MTyxOXtBEysguc97jJveUZG0K97aIKG6xVUlMutg5yxhw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/options@5.5.1': + resolution: {integrity: sha512-eo971c9iLNLmk+yOFyo7yKIJzJ/zou6uKpy6mBuyb/thKtS/haiKIc3VLhyTXty3OH2PW8yOlORJnv4DexJB8A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/plugin-core@5.5.1': + resolution: {integrity: sha512-VUZl30lDQFJeiSyNfzU1EjYt2QZvoBFKEwjn1lilUJw7KgqD5z7mbV7diJhT+dLFs36i0OsjXvq5kSygn8YJ3A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/programs@5.5.1': + resolution: {integrity: sha512-7U9kn0Jsx1NuBLn5HRTFYh78MV4XN145Yc3WP/q5BlqAVNlMoU9coG5IUTJIG847TUqC1lRto3Dnpwm6T4YRpA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/promises@5.5.1': + resolution: {integrity: sha512-T9lfuUYkGykJmppEcssNiCf6yiYQxJkhiLPP+pyAc2z84/7r3UVIb2tNJk4A9sucS66pzJnVHZKcZVGUUp6wzA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-api@5.5.1': + resolution: {integrity: sha512-XWOQQPhKl06Vj0xi3RYHAc6oEQd8B82okYJ04K7N0Vvy3J4PN2cxeK7klwkjgavdcN9EVkYCChm2ADAtnztKnA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-parsed-types@5.5.1': + resolution: {integrity: sha512-HEi3G2nZqGEsa3vX6U0FrXLaqnUCg4SKIUrOe8CezD+cSFbRTOn3rCLrUmJrhVyXlHoQVaRO9mmeovk31jWxJg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec-types@5.5.1': + resolution: {integrity: sha512-6OFKtRpIEJQs8Jb2C4OO8KyP2h2Hy1MFhatMAoXA+0Ik8S3H+CicIuMZvGZ91mIu/tXicuOOsNNLu3HAkrakrw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-spec@5.5.1': + resolution: {integrity: sha512-m3LX2bChm3E3by4mQrH4YwCAFY57QBzuUSWqlUw7ChuZ+oLLOq7b2czi4i6L4Vna67j3eCmB3e+4tqy1j5wy7Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-api@5.5.1': + resolution: {integrity: sha512-5Oi7k+GdeS8xR2ly1iuSFkAv6CZqwG0Z6b1QZKbEgxadE1XGSDrhM2cn59l+bqCozUWCqh4c/A2znU/qQjROlw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-channel-websocket@5.5.1': + resolution: {integrity: sha512-7tGfBBrYY8TrngOyxSHoCU5shy86iA9SRMRrPSyBhEaZRAk6dnbdpmUTez7gtdVo0BCvh9nzQtUycKWSS7PnFQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions-spec@5.5.1': + resolution: {integrity: sha512-iq+rGq5fMKP3/mKHPNB6MC8IbVW41KGZg83Us/+LE3AWOTWV1WT20KT2iH1F1ik9roi42COv/TpoZZvhKj45XQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-subscriptions@5.5.1': + resolution: {integrity: sha512-CTMy5bt/6mDh4tc6vUJms9EcuZj3xvK0/xq8IQ90rhkpYvate91RjBP+egvjgSayUg9yucU9vNuUpEjz4spM7w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transformers@5.5.1': + resolution: {integrity: sha512-OsWqLCQdcrRJKvHiMmwFhp9noNZ4FARuMkHT5us3ustDLXaxOjF0gfqZLnMkulSLcKt7TGXqMhBV+HCo7z5M8Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-transport-http@5.5.1': + resolution: {integrity: sha512-yv8GoVSHqEV0kUJEIhkdOVkR2SvJ6yoWC51cJn2rSV7plr6huLGe0JgujCmB7uZhhaLbcbP3zxXxu9sOjsi7Fg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-types@5.5.1': + resolution: {integrity: sha512-bibTFQ7PbHJJjGJPmfYC2I+/5CRFS4O2p9WwbFraX1Keeel+nRrt/NBXIy8veP5AEn2sVJIyJPpWBRpCx1oATA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc@5.5.1': + resolution: {integrity: sha512-ku8zTUMrkCWci66PRIBC+1mXepEnZH/q1f3ck0kJZ95a06bOTl5KU7HeXWtskkyefzARJ5zvCs54AD5nxjQJ+A==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/signers@5.5.1': + resolution: {integrity: sha512-FY0IVaBT2kCAze55vEieR6hag4coqcuJ31Aw3hqRH7mv6sV8oqwuJmUrx+uFwOp1gwd5OEAzlv6N4hOOple4sQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/subscribable@5.5.1': + resolution: {integrity: sha512-9K0PsynFq0CsmK1CDi5Y2vUIJpCqkgSS5yfDN0eKPgHqEptLEaia09Kaxc90cSZDZU5mKY/zv1NBmB6Aro9zQQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/sysvars@5.5.1': + resolution: {integrity: sha512-k3Quq87Mm+geGUu1GWv6knPk0ALsfY6EKSJGw9xUJDHzY/RkYSBnh0RiOrUhtFm2TDNjOailg8/m0VHmi3reFA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-confirmation@5.5.1': + resolution: {integrity: sha512-j4mKlYPHEyu+OD7MBt3jRoX4ScFgkhZC6H65on4Fux6LMScgivPJlwnKoZMnsgxFgWds0pl+BYzSiALDsXlYtw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transaction-messages@5.5.1': + resolution: {integrity: sha512-aXyhMCEaAp3M/4fP0akwBBQkFPr4pfwoC5CLDq999r/FUwDax2RE/h4Ic7h2Xk+JdcUwsb+rLq85Y52hq84XvQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/transactions@5.5.1': + resolution: {integrity: sha512-8hHtDxtqalZ157pnx6p8k10D7J/KY/biLzfgh9R09VNLLY3Fqi7kJvJCr7M2ik3oRll56pxhraAGCC9yIT6eOA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/plugin-svgo@8.1.0': + resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/rollup@8.1.0': + resolution: {integrity: sha512-0XR1poYvPQoPpmfDYLEqUGu5ePAQ4pdgN3VFsZBNAeze7qubVpsIY1o1R6PZpKep/DKu33GSm2NhwpCLkMs2Cw==} + engines: {node: '>=14'} + + '@swc/core-darwin-arm64@1.15.40': + resolution: {integrity: sha512-PaYyclfmQ++77D8ityYvmmVzHv9aG8ROwt2GfG6/ccloy4Hgf80qtOnzb9VYvPsUT7Ty1uhuDRhv3XYpf62qhQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.15.40': + resolution: {integrity: sha512-HbbPzvfLBUXjIB1Ezks+//lNUjmLjfyd63XSwprJgrZaXYdm70kohXPJUWdqKZozolFxbPaO+xtBaiUp6BoueA==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.15.40': + resolution: {integrity: sha512-SlRZsCjOCPR2LvFs0Ri/Xrx/5o5TCt8vl4gW6mX1hEZOG0a625RxzRHpHdAQNGykmAN/7IeaFAJG+QnNmxlHcA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.15.40': + resolution: {integrity: sha512-Q8byxJt2fh8CR3EUX6snBpy47AoBVm+In/+Z3rjDHMjC38ZvR9/gtUUNCT0tfrn4EdVsO8/QPi59nxrxvqxvBQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.15.40': + resolution: {integrity: sha512-4z0MgHU+7M0pZDqBN1El7mFXDI1SBwinfcUkAyA4v8QrhOIUOZltySt2aStQLZGrdXVXM4Y4ylfiTC04ED+MoQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-ppc64-gnu@1.15.40': + resolution: {integrity: sha512-fLI4iUgeSZu0eRWUXwe6YzPFx9gHbFiPkl8Rp3mJfP8OpNR3nTQCGPvHdDh9xniW7mVvgMY4ni7A4VzqI1KrpA==} + engines: {node: '>=10'} + cpu: [ppc64] + os: [linux] + + '@swc/core-linux-s390x-gnu@1.15.40': + resolution: {integrity: sha512-YqeKMAb7d4nQSGMJQ454IlaCENpzcDqhvBE9+CPfdnYpnUXxd+BSrB6Xk0YjW8UyoEhUj4p6quATCxbsp6J3jg==} + engines: {node: '>=10'} + cpu: [s390x] + os: [linux] + + '@swc/core-linux-x64-gnu@1.15.40': + resolution: {integrity: sha512-7HOuS1iGcme/j/TuL1TfmmLGiMQrjv/GmjyZeydl00FKPtpGXEldwqfI56xgd1YzrzoB2svWjxbGGyQ0TEASxg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.15.40': + resolution: {integrity: sha512-h4kZYHc7dpc9P9u4brRJaS8Pl7tPVHAeiLSzw7T5RfIJgAoSdaCMKzI/2Uay9gFhaw8uyCDl0L5q37r0EpAfIA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.15.40': + resolution: {integrity: sha512-+mQgKZXSj6mV38Zh05QaxSjUDmGP/R2JWlXZTDLSPkDzHU6p3GxN9eeSf5dfyDVU86946fmCvSzyl/ucImx8+A==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.40': + resolution: {integrity: sha512-yvwdPLGd25mcj/mNatjNQ0lZujtQD6psH3v9PNmMb+fSzjbNG8KIDxjFWrcV+fsFVLOkyOmdJsFmX7NAFjVyPw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.40': + resolution: {integrity: sha512-OXtKsLU1bVtInzzDEAY2sYiF/rl4tvAnLLLpuMp3HzAOQZ5A+i69AKDhA1YLQTaMAqO3vzyYNVAYVRMPtSYD4w==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.40': + resolution: {integrity: sha512-2kwzJikRvgtNAG7MwVZY2vEzZjTxKIq5jXOihuSV/8U+Hej8Va22t65aKnJZs3P+NwojZvR8Mf8kyM7O+V8sQg==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.26': + resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==} + + '@swc/wasm@1.15.40': + resolution: {integrity: sha512-FVS3SEJXBxjpxVUGSzaTaCdJjnXUalRftA/6hILMAJIcYHBoiBfJlxuH6s47iajlAJZP25e5Kf4HNHvvwyOEgw==} + + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + + '@tanstack/query-core@5.100.14': + resolution: {integrity: sha512-5X41dGpxgeaHISCRW2oYwcSycZeULZzAunaudXT9ov1KOTj9xwt0CH6hbwqP1/z74ZWF7rYFnDpyYH07XFcZew==} + + '@tanstack/react-query@5.100.14': + resolution: {integrity: sha512-oOr6aRdSFEwWhzxEkD/9ZcItM3+LjBSkeVmadWKwUssAHTsqd/7bOjWrX4AbvEkoEhgAxzN0Xk6H/aYzXiYBAw==} + peerDependencies: + react: ^18 || ^19 + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/lodash@4.17.24': + resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@22.7.5': + resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react-syntax-highlighter@15.5.13': + resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} + + '@types/react@18.3.29': + resolution: {integrity: sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@wagmi/connectors@6.2.0': + resolution: {integrity: sha512-2NfkbqhNWdjfibb4abRMrn7u6rPjEGolMfApXss6HCDVt9AW2oVC6k8Q5FouzpJezElxLJSagWz9FW1zaRlanA==} + peerDependencies: + '@wagmi/core': 2.22.1 + typescript: '>=5.0.4' + viem: 2.x + peerDependenciesMeta: + typescript: + optional: true + + '@wagmi/core@2.22.1': + resolution: {integrity: sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg==} + peerDependencies: + '@tanstack/query-core': '>=5.0.0' + typescript: '>=5.0.4' + viem: 2.x + peerDependenciesMeta: + '@tanstack/query-core': + optional: true + typescript: + optional: true + + '@walletconnect/core@2.21.0': + resolution: {integrity: sha512-o6R7Ua4myxR8aRUAJ1z3gT9nM+jd2B2mfamu6arzy1Cc6vi10fIwFWb6vg3bC8xJ6o9H3n/cN5TOW3aA9Y1XVw==} + engines: {node: '>=18'} + + '@walletconnect/core@2.21.1': + resolution: {integrity: sha512-Tp4MHJYcdWD846PH//2r+Mu4wz1/ZU/fr9av1UWFiaYQ2t2TPLDiZxjLw54AAEpMqlEHemwCgiRiAmjR1NDdTQ==} + engines: {node: '>=18'} + + '@walletconnect/environment@1.0.1': + resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==} + + '@walletconnect/ethereum-provider@2.21.1': + resolution: {integrity: sha512-SSlIG6QEVxClgl1s0LMk4xr2wg4eT3Zn/Hb81IocyqNSGfXpjtawWxKxiC5/9Z95f1INyBD6MctJbL/R1oBwIw==} + + '@walletconnect/events@1.0.1': + resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} + + '@walletconnect/heartbeat@1.2.2': + resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==} + + '@walletconnect/jsonrpc-http-connection@1.0.8': + resolution: {integrity: sha512-+B7cRuaxijLeFDJUq5hAzNyef3e3tBDIxyaCNmFtjwnod5AGis3RToNqzFU33vpVcxFhofkpE7Cx+5MYejbMGw==} + + '@walletconnect/jsonrpc-provider@1.0.14': + resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==} + + '@walletconnect/jsonrpc-types@1.0.4': + resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==} + + '@walletconnect/jsonrpc-utils@1.0.8': + resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==} + + '@walletconnect/jsonrpc-ws-connection@1.0.16': + resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==} + + '@walletconnect/keyvaluestorage@1.1.1': + resolution: {integrity: sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==} + peerDependencies: + '@react-native-async-storage/async-storage': 1.x + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true + + '@walletconnect/logger@2.1.2': + resolution: {integrity: sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==} + + '@walletconnect/relay-api@1.0.11': + resolution: {integrity: sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==} + + '@walletconnect/relay-auth@1.1.0': + resolution: {integrity: sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==} + + '@walletconnect/safe-json@1.0.2': + resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==} + + '@walletconnect/sign-client@2.21.0': + resolution: {integrity: sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==} + + '@walletconnect/sign-client@2.21.1': + resolution: {integrity: sha512-QaXzmPsMnKGV6tc4UcdnQVNOz4zyXgarvdIQibJ4L3EmLat73r5ZVl4c0cCOcoaV7rgM9Wbphgu5E/7jNcd3Zg==} + + '@walletconnect/time@1.0.2': + resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} + + '@walletconnect/types@2.21.0': + resolution: {integrity: sha512-ll+9upzqt95ZBWcfkOszXZkfnpbJJ2CmxMfGgE5GmhdxxxCcO5bGhXkI+x8OpiS555RJ/v/sXJYMSOLkmu4fFw==} + + '@walletconnect/types@2.21.1': + resolution: {integrity: sha512-UeefNadqP6IyfwWC1Yi7ux+ljbP2R66PLfDrDm8izmvlPmYlqRerJWJvYO4t0Vvr9wrG4Ko7E0c4M7FaPKT/sQ==} + + '@walletconnect/universal-provider@2.21.0': + resolution: {integrity: sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==} + + '@walletconnect/universal-provider@2.21.1': + resolution: {integrity: sha512-Wjx9G8gUHVMnYfxtasC9poGm8QMiPCpXpbbLFT+iPoQskDDly8BwueWnqKs4Mx2SdIAWAwuXeZ5ojk5qQOxJJg==} + + '@walletconnect/utils@2.21.0': + resolution: {integrity: sha512-zfHLiUoBrQ8rP57HTPXW7rQMnYxYI4gT9yTACxVW6LhIFROTF6/ytm5SKNoIvi4a5nX5dfXG4D9XwQUCu8Ilig==} + + '@walletconnect/utils@2.21.1': + resolution: {integrity: sha512-VPZvTcrNQCkbGOjFRbC24mm/pzbRMUq2DSQoiHlhh0X1U7ZhuIrzVtAoKsrzu6rqjz0EEtGxCr3K1TGRqDG4NA==} + + '@walletconnect/window-getters@1.0.1': + resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==} + + '@walletconnect/window-metadata@1.0.1': + resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} + + '@zk-kit/lean-imt@2.2.4': + resolution: {integrity: sha512-uoRl99DID9Z5EuhfecDuIGP0KLrGEndVH0+texwVQBmlXTvve+grkKT4w06C+VFzRmfYNVuRo9tXvqIzQ3dqPA==} + + '@zk-kit/utils@1.4.1': + resolution: {integrity: sha512-IoXzy3ElfkvRfC/s6uk3eFQO8uxkCdAOVxKlJlaEjM7pgckX5AK46Q+7n5yMQD/v/o6OL0mMtet065o5EpUocA==} + + abitype@1.0.6: + resolution: {integrity: sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.0.8: + resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.1.0: + resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + abitype@1.2.4: + resolution: {integrity: sha512-dpKH+N27vRjarMVTFFkeY445VTKftzGWpL0FiT7xmVmzQRKazZexzC5uHG0f6XKsVLAuUlndnbGau6lRejClxg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + add@2.0.6: + resolution: {integrity: sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==} + + aes-js@4.0.0-beta.5: + resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + asn1.js@4.10.1: + resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + + assert@2.1.0: + resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + + async-mutex@0.2.6: + resolution: {integrity: sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + autoprefixer@10.5.0: + resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios-retry@4.5.0: + resolution: {integrity: sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==} + peerDependencies: + axios: 0.x || 1.x + + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + + axios@1.16.0: + resolution: {integrity: sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-styled-components@2.3.0: + resolution: {integrity: sha512-nP/y6PbBqS/qtKROnJCgpGo8hYUzlBAVXN1QAjSBANL6vZiQXPQN7FYW/nUwoxY7nZhBEGm9T5tjL9gbzwulDw==} + peerDependencies: + '@babel/core': ^7.0.0 + styled-components: '>= 2' + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + base-x@5.0.1: + resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.10.32: + resolution: {integrity: sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==} + engines: {node: '>=6.0.0'} + hasBin: true + + big.js@6.2.2: + resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bn.js@4.12.3: + resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==} + + bn.js@5.2.3: + resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + browser-resolve@2.0.0: + resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.1: + resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} + engines: {node: '>= 0.10'} + + browserify-sign@4.2.5: + resolution: {integrity: sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==} + engines: {node: '>= 0.10'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bufferutil@4.1.0: + resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} + engines: {node: '>=6.14.2'} + + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + charenc@0.0.2: + resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + cipher-base@1.0.7: + resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} + engines: {node: '>= 0.10'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comlink@4.4.2: + resolution: {integrity: sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==} + + comma-separated-tokens@1.0.8: + resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + engines: {node: '>=20'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + connectkit@1.9.2: + resolution: {integrity: sha512-ZaYdL2UgHdcVy5j7Cn5C6KUtA5Ev3LEc3mPAzEoH3V3hdu3CdC8jgFTFsgInTXXpsdNpwTEEDPnP245ok7wOYg==} + engines: {node: '>=12.4'} + peerDependencies: + '@tanstack/react-query': '>=5.0.0' + react: 17.x || 18.x + react-dom: 17.x || 18.x + viem: 2.x + wagmi: 2.x + + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + + core-js-compat@3.49.0: + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + crypt@0.0.2: + resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + + crypto-browserify@3.12.1: + resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==} + engines: {node: '>= 0.10'} + + css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + derive-valtio@0.1.0: + resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} + peerDependencies: + valtio: '*' + + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-browser@5.3.0: + resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domain-browser@4.22.0: + resolution: {integrity: sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==} + engines: {node: '>=10'} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + + eciesjs@0.4.18: + resolution: {integrity: sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==} + engines: {bun: '>=1', deno: '>=2', node: '>=16'} + + electron-to-chromium@1.5.361: + resolution: {integrity: sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==} + + elliptic@6.6.1: + resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + + email-addresses@5.0.0: + resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encode-utf8@1.0.3: + resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + engine.io-client@6.6.5: + resolution: {integrity: sha512-QCwxUDULPlXv8F6tqMMKx5dNkTe6OaBYRMPYeXKBlyOoKvAmE0ac6pW7fFhSscJ/5SI7666/U/B+MElbsrJlIg==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-toolkit@1.33.0: + resolution: {integrity: sha512-X13Q/ZSc+vsO1q600bvNK4bxgXMkHcf//RxCmYDaRY5DAcT+eoXjY5hoAPGMdRnWQjvyLEcyauG3b6hz76LNqg==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eth-block-tracker@7.1.0: + resolution: {integrity: sha512-8YdplnuE1IK4xfqpf4iU7oBxnOYAc35934o083G8ao+8WM8QQtt/mVlAY6yIAdY1eMeLqg4Z//PZjJGmWGPMRg==} + engines: {node: '>=14.0.0'} + + eth-json-rpc-filters@6.0.1: + resolution: {integrity: sha512-ITJTvqoCw6OVMLs7pI8f4gG92n/St6x80ACtHodeS+IXmO0w+t1T5OOzfSt7KLSMLRkVUoexV7tztLgDxg+iig==} + engines: {node: '>=14.0.0'} + + eth-query@2.1.2: + resolution: {integrity: sha512-srES0ZcvwkR/wd5OQBRA1bIJMww1skfGS0s8wlwK3/oNP4+wnds60krvu5R1QbpRQjMmpG5OMIWro5s7gvDPsA==} + + eth-rpc-errors@4.0.3: + resolution: {integrity: sha512-Z3ymjopaoft7JDoxZcEb3pwdGh7yiYMhOwm2doUt6ASXlMavpNlK6Cre0+IMl2VSGyEU9rkiperQhp5iRxn5Pg==} + + ethereum-cryptography@2.2.1: + resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} + + ethers@6.13.5: + resolution: {integrity: sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ==} + engines: {node: '>=14.0.0'} + + ethers@6.16.0: + resolution: {integrity: sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==} + engines: {node: '>=14.0.0'} + + eventemitter2@6.4.9: + resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extension-port-stream@3.0.0: + resolution: {integrity: sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + filename-reserved-regex@2.0.0: + resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==} + engines: {node: '>=4'} + + filenamify@4.3.0: + resolution: {integrity: sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==} + engines: {node: '>=8'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + filter-obj@1.1.0: + resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} + engines: {node: '>=0.10.0'} + + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + format@0.2.2: + resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} + engines: {node: '>=0.4.x'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + framer-motion@6.5.1: + resolution: {integrity: sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==} + peerDependencies: + react: '>=16.8 || ^17.0.0 || ^18.0.0' + react-dom: '>=16.8 || ^17.0.0 || ^18.0.0' + + framesync@6.0.1: + resolution: {integrity: sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==} + + fs-extra@11.3.5: + resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==} + engines: {node: '>=14.14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gh-pages@6.3.0: + resolution: {integrity: sha512-Ot5lU6jK0Eb+sszG8pciXdjMXdBJ5wODvgjR+imihTqsUWF2K6dJ9HST55lgqcs8wWcw6o6wAsUzfcYRhJPXbA==} + engines: {node: '>=10'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hash-base@3.0.5: + resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} + engines: {node: '>= 0.10'} + + hash-base@3.1.2: + resolution: {integrity: sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==} + engines: {node: '>= 0.8'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + + hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + highlightjs-vue@1.0.0: + resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hono@4.12.22: + resolution: {integrity: sha512-7fvVPbB92zNRsQke+uiRGwtTuef0tB2Dg4hWxYfFNvkQhIltWoyi0ONReM5LWA+jJWS3nfT5lTq+qbsIpX0IQw==} + engines: {node: '>=16.9.0'} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + + idb-keyval@6.2.1: + resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} + + idb-keyval@6.2.4: + resolution: {integrity: sha512-D/NzHWUmYJGXi++z67aMSrnisb9A3621CyRK5G89JyTlN13C8xf0g04DLxUKMufPem3e3L2JAXR6Z00OWy183Q==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-nan@1.3.2: + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-retry-allowed@2.2.0: + resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} + engines: {node: '>=10'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isomorphic-timers-promises@1.0.1: + resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} + engines: {node: '>=10'} + + isows@1.0.6: + resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} + peerDependencies: + ws: '*' + + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-rpc-engine@6.1.0: + resolution: {integrity: sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==} + engines: {node: '>=10.0.0'} + + json-rpc-random-id@1.0.1: + resolution: {integrity: sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + keccak@3.0.4: + resolution: {integrity: sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==} + engines: {node: '>=10.0.0'} + + keyvaluestorage-interface@1.0.0: + resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lit-element@4.2.2: + resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} + + lit-html@3.3.3: + resolution: {integrity: sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==} + + lit@3.3.0: + resolution: {integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + + lru-cache@11.5.0: + resolution: {integrity: sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + + md5@2.3.0: + resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micro-ftch@0.3.1: + resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + mipd@0.0.7: + resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.12: + resolution: {integrity: sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==} + + multiformats@9.9.0: + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-addon-api@2.0.2: + resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + + node-releases@2.0.46: + resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} + engines: {node: '>=18'} + + node-stdlib-browser@1.3.1: + resolution: {integrity: sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw==} + engines: {node: '>=10'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + obj-multiplex@1.0.0: + resolution: {integrity: sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + on-exit-leak-free@0.2.0: + resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + openapi-fetch@0.13.8: + resolution: {integrity: sha512-yJ4QKRyNxE44baQ9mY5+r/kAzZ8yXMemtNAOFwOzRXJscdjSxxzWSNlyBAr+o5JjkUw9Lc3W7OIoca0cY3PYnQ==} + + openapi-typescript-helpers@0.0.15: + resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==} + + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + + ox@0.14.22: + resolution: {integrity: sha512-nb5msL8qWbPglhIfZbGJAfw3cqiJjFMiWmACt7kgyWtLib12tcctbHufMT9Hb0Lr6Pt4k9I3dbpueTpbhvbqvA==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.6.7: + resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.6.9: + resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.7.1: + resolution: {integrity: sha512-+k9fY9PRNuAMHRFIUbiK9Nt5seYHHzSQs9Bj+iMETcGtlpS7SmBzcGSVUQO3+nqGLEiNK4598pHNFlVRaZbRsg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.9.17: + resolution: {integrity: sha512-rKAnhzhRU3Xh3hiko+i1ZxywZ55eWQzeS/Q4HRKLx2PqfHOolisZHErSsJVipGlmQKHW5qwOED/GighEw9dbLg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.9.6: + resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-asn1@5.1.9: + resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} + engines: {node: '>= 0.10'} + + parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pbkdf2@3.1.5: + resolution: {integrity: sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==} + engines: {node: '>= 0.10'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pify@5.0.0: + resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} + engines: {node: '>=10'} + + pino-abstract-transport@0.5.0: + resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==} + + pino-std-serializers@4.0.0: + resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==} + + pino@7.11.0: + resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} + hasBin: true + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-dir@5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + + pony-cause@2.1.11: + resolution: {integrity: sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==} + engines: {node: '>=12.0.0'} + + popmotion@11.0.3: + resolution: {integrity: sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==} + + porto@0.2.35: + resolution: {integrity: sha512-gu9FfjjvvYBgQXUHWTp6n3wkTxVtEcqFotM7i3GEZeoQbvLGbssAicCz6hFZ8+xggrJWwi/RLmbwNra50SMmUQ==} + hasBin: true + peerDependencies: + '@tanstack/react-query': '>=5.59.0' + '@wagmi/core': '>=2.16.3' + expo-auth-session: '>=7.0.8' + expo-crypto: '>=15.0.7' + expo-web-browser: '>=15.0.8' + react: '>=18' + react-native: '>=0.81.4' + typescript: '>=5.4.0' + viem: '>=2.37.0' + wagmi: '>=2.0.0' + peerDependenciesMeta: + '@tanstack/react-query': + optional: true + expo-auth-session: + optional: true + expo-crypto: + optional: true + expo-web-browser: + optional: true + react: + optional: true + react-native: + optional: true + typescript: + optional: true + wagmi: + optional: true + + poseidon-lite@0.3.0: + resolution: {integrity: sha512-ilJj4MIve4uBEG7SrtPqUUNkvpJ/pLVbndxa0WvebcQqeIhe+h72JR4g0EvwchUzm9sOQDlOjiDNmRAgxNZl4A==} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.24.2: + resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} + + preact@10.29.2: + resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} + + prettier-plugin-tailwindcss@0.5.14: + resolution: {integrity: sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig-melody': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig-melody': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} + engines: {node: '>=14'} + hasBin: true + + prismjs@1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process-warning@1.0.0: + resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + property-information@5.6.0: + resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + proxy-compare@2.6.0: + resolution: {integrity: sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + qrcode@1.5.3: + resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} + engines: {node: '>=10.13.0'} + hasBin: true + + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + + qs@6.15.2: + resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} + engines: {node: '>=0.6'} + + query-string@7.1.3: + resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} + engines: {node: '>=6'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-markdown@9.1.0: + resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-router-dom@6.30.3: + resolution: {integrity: sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.30.3: + resolution: {integrity: sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-syntax-highlighter@15.6.6: + resolution: {integrity: sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==} + peerDependencies: + react: '>= 0.14.0' + + react-transition-state@1.1.5: + resolution: {integrity: sha512-ITY2mZqc2dWG2eitJkYNdcSFW8aKeOlkL2A/vowRrLL8GH3J6Re/SpD/BLvQzrVOTqjsP0b5S9N10vgNNzwMUQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + react-use-measure@2.1.7: + resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} + peerDependencies: + react: '>=16.13' + react-dom: '>=16.13' + peerDependenciesMeta: + react-dom: + optional: true + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + real-require@0.1.0: + resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==} + engines: {node: '>= 12.13.0'} + + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.1: + resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} + hasBin: true + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + ripemd160@2.0.3: + resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} + engines: {node: '>= 0.8'} + + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + engines: {node: '>=10'} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + sha.js@2.4.12: + resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} + engines: {node: '>= 0.10'} + hasBin: true + + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + socket.io-client@4.8.3: + resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.6: + resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} + engines: {node: '>=10.0.0'} + + sonic-boom@2.8.0: + resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@1.1.5: + resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + split-on-first@1.1.0: + resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} + engines: {node: '>=6'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + stream-http@3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + + strict-uri-encode@2.0.0: + resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} + engines: {node: '>=4'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-outer@1.0.1: + resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} + engines: {node: '>=0.10.0'} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + style-value-types@5.0.0: + resolution: {integrity: sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==} + + styled-components@5.3.11: + resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + react-is: '>= 16.8.0' + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + superstruct@1.0.4: + resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==} + engines: {node: '>=14.0.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + svgo@3.3.3: + resolution: {integrity: sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==} + engines: {node: '>=14.0.0'} + hasBin: true + + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + thread-stream@0.15.2: + resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} + + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + to-buffer@1.2.2: + resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} + engines: {node: '>= 0.4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trim-repeated@1.0.0: + resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} + engines: {node: '>=0.10.0'} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tty-browserify@0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + uint8arrays@3.1.0: + resolution: {integrity: sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + undici-types@7.25.0: + resolution: {integrity: sha512-AXNgS1Byr27fTI+2bsPEkV9CxkT8H6xNyRI68b3TatlZo3RkzlqQBLL+w7SmGPVpokjHbcuNVQUWE7FRTg+LRA==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unstorage@1.17.5: + resolution: {integrity: sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + + use-sync-external-store@1.2.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + use-sync-external-store@1.4.0: + resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + + valtio@1.13.2: + resolution: {integrity: sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=16.8' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + viem@2.23.2: + resolution: {integrity: sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + viem@2.30.6: + resolution: {integrity: sha512-N3vGy3pZ+EVgQRuWqQhZPFXxQE8qMRrBd3uM+KLc1Ub2w6+vkyr7umeWQCM4c+wlsCdByUgh2630MDMLquMtpg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + viem@2.38.6: + resolution: {integrity: sha512-aqO6P52LPXRjdnP6rl5Buab65sYa4cZ6Cpn+k4OLOzVJhGIK8onTVoKMFMT04YjDfyDICa/DZyV9HmvLDgcjkw==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + viem@2.50.4: + resolution: {integrity: sha512-rf98F4s3Vlb+uJZEKfay3IbBw3CNCbVtx5Y3UIljlO2tSX420g/J0WQSYsjzBSasUFgxgsXabji14O9kGbiqgg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + vite-plugin-node-polyfills@0.22.0: + resolution: {integrity: sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA==} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + + vite-plugin-top-level-await@1.6.0: + resolution: {integrity: sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww==} + peerDependencies: + vite: '>=2.8' + + vite-tsconfig-paths@4.3.2: + resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + + wagmi@2.19.5: + resolution: {integrity: sha512-RQUfKMv6U+EcSNNGiPbdkDtJwtuFxZWLmvDiQmjjBgkuPulUwDJsKhi7gjynzJdsx2yDqhHCXkKsbbfbIsHfcQ==} + peerDependencies: + '@tanstack/react-query': '>=5.0.0' + react: '>=18' + typescript: '>=5.0.4' + viem: 2.x + peerDependenciesMeta: + typescript: + optional: true + + webextension-polyfill@0.10.0: + resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@7.5.11: + resolution: {integrity: sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.2: + resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.20.1: + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zustand@5.0.0: + resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zustand@5.0.13: + resolution: {integrity: sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zustand@5.0.3: + resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@aave/account@0.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))': + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + + '@adraffy/ens-normalize@1.10.1': {} + + '@adraffy/ens-normalize@1.11.1': {} + + '@alloc/quick-lru@5.2.0': {} + + '@aztec/bb.js@3.0.0-nightly.20260102': + dependencies: + comlink: 4.4.2 + commander: 12.1.0 + idb-keyval: 6.2.4 + msgpackr: 1.11.12 + pako: 2.1.0 + tslib: 2.8.1 + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3(supports-color@5.5.0) + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.28.6(supports-color@5.5.0)': + dependencies: + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.29.0 + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/traverse': 7.29.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/preset-env@7.29.5(@babel/core@7.29.0)': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.3(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.29.0) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/types': 7.29.0 + esutils: 2.0.3 + + '@babel/preset-react@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0(supports-color@5.5.0)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@base-org/account@2.4.0(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@coinbase/cdp-sdk': 1.50.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + preact: 10.24.2 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.3(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - debug + - fastestsmallesttextencoderdecoder + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + + '@coinbase/cdp-sdk@1.50.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana-program/system': 0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana-program/token': 0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana/kit': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + abitype: 1.0.6(typescript@5.9.3)(zod@3.25.76) + axios: 1.16.0 + axios-retry: 4.5.0(axios@1.16.0) + bs58: 6.0.0 + jose: 6.2.3 + md5: 2.3.0 + uncrypto: 0.1.3 + viem: 2.50.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - debug + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + + '@coinbase/wallet-sdk@3.9.3': + dependencies: + bn.js: 5.2.3 + buffer: 6.0.3 + clsx: 1.2.1 + eth-block-tracker: 7.1.0 + eth-json-rpc-filters: 6.0.1 + eventemitter3: 5.0.4 + keccak: 3.0.4 + preact: 10.29.2 + sha.js: 2.4.12 + transitivePeerDependencies: + - supports-color + + '@coinbase/wallet-sdk@4.3.6(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/hashes': 1.4.0 + clsx: 1.2.1 + eventemitter3: 5.0.1 + idb-keyval: 6.2.1 + ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + preact: 10.24.2 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.3(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - immer + - react + - typescript + - use-sync-external-store + - utf-8-validate + - zod + + '@crisp-e3/sdk@0.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@aztec/bb.js': 3.0.0-nightly.20260102 + '@crisp-e3/zk-inputs': 0.9.0 + '@noir-lang/noir_js': 1.0.0-beta.16 + '@zk-kit/lean-imt': 2.2.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + poseidon-lite: 0.3.0 + viem: 2.30.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@crisp-e3/zk-inputs@0.9.0': {} + + '@ecies/ciphers@0.2.6(@noble/ciphers@1.3.0)': + dependencies: + '@noble/ciphers': 1.3.0 + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/runtime': 7.29.2 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@0.8.8': + dependencies: + '@emotion/memoize': 0.7.4 + optional: true + + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.7.4': + optional: true + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@18.3.29)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.29 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.2.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/stylis@0.8.5': {} + + '@emotion/unitless@0.10.0': {} + + '@emotion/unitless@0.7.5': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@ethereumjs/common@3.2.0': + dependencies: + '@ethereumjs/util': 8.1.0 + crc-32: 1.2.2 + + '@ethereumjs/rlp@4.0.1': {} + + '@ethereumjs/tx@4.2.0': + dependencies: + '@ethereumjs/common': 3.2.0 + '@ethereumjs/rlp': 4.0.1 + '@ethereumjs/util': 8.1.0 + ethereum-cryptography: 2.2.1 + + '@ethereumjs/util@8.1.0': + dependencies: + '@ethereumjs/rlp': 4.0.1 + ethereum-cryptography: 2.2.1 + micro-ftch: 0.3.1 + + '@gemini-wallet/core@0.3.2(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + '@metamask/rpc-errors': 7.0.2 + eventemitter3: 5.0.1 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@lit-labs/ssr-dom-shim@1.6.0': {} + + '@lit/reactive-element@2.1.2': + dependencies: + '@lit-labs/ssr-dom-shim': 1.6.0 + + '@metamask/eth-json-rpc-provider@1.0.1': + dependencies: + '@metamask/json-rpc-engine': 7.3.3 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 5.0.2 + transitivePeerDependencies: + - supports-color + + '@metamask/json-rpc-engine@7.3.3': + dependencies: + '@metamask/rpc-errors': 6.4.0 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + transitivePeerDependencies: + - supports-color + + '@metamask/json-rpc-engine@8.0.2': + dependencies: + '@metamask/rpc-errors': 6.4.0 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + transitivePeerDependencies: + - supports-color + + '@metamask/json-rpc-middleware-stream@7.0.2': + dependencies: + '@metamask/json-rpc-engine': 8.0.2 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + readable-stream: 3.6.2 + transitivePeerDependencies: + - supports-color + + '@metamask/object-multiplex@2.1.0': + dependencies: + once: 1.4.0 + readable-stream: 3.6.2 + + '@metamask/onboarding@1.0.1': + dependencies: + bowser: 2.14.1 + + '@metamask/providers@16.1.0': + dependencies: + '@metamask/json-rpc-engine': 8.0.2 + '@metamask/json-rpc-middleware-stream': 7.0.2 + '@metamask/object-multiplex': 2.1.0 + '@metamask/rpc-errors': 6.4.0 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 8.5.0 + detect-browser: 5.3.0 + extension-port-stream: 3.0.0 + fast-deep-equal: 3.1.3 + is-stream: 2.0.1 + readable-stream: 3.6.2 + webextension-polyfill: 0.10.0 + transitivePeerDependencies: + - supports-color + + '@metamask/rpc-errors@6.4.0': + dependencies: + '@metamask/utils': 9.3.0 + fast-safe-stringify: 2.1.1 + transitivePeerDependencies: + - supports-color + + '@metamask/rpc-errors@7.0.2': + dependencies: + '@metamask/utils': 11.11.0 + fast-safe-stringify: 2.1.1 + transitivePeerDependencies: + - supports-color + + '@metamask/safe-event-emitter@2.0.0': {} + + '@metamask/safe-event-emitter@3.1.2': {} + + '@metamask/sdk-analytics@0.0.5': + dependencies: + openapi-fetch: 0.13.8 + + '@metamask/sdk-communication-layer@0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.18)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + dependencies: + '@metamask/sdk-analytics': 0.0.5 + bufferutil: 4.1.0 + cross-fetch: 4.1.0 + date-fns: 2.30.0 + debug: 4.3.4 + eciesjs: 0.4.18 + eventemitter2: 6.4.9 + readable-stream: 3.6.2 + socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + utf-8-validate: 5.0.10 + uuid: 8.3.2 + transitivePeerDependencies: + - supports-color + + '@metamask/sdk-install-modal-web@0.32.1': + dependencies: + '@paulmillr/qr': 0.2.1 + + '@metamask/sdk@0.33.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + dependencies: + '@babel/runtime': 7.29.2 + '@metamask/onboarding': 1.0.1 + '@metamask/providers': 16.1.0 + '@metamask/sdk-analytics': 0.0.5 + '@metamask/sdk-communication-layer': 0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.18)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@metamask/sdk-install-modal-web': 0.32.1 + '@paulmillr/qr': 0.2.1 + bowser: 2.14.1 + cross-fetch: 4.1.0 + debug: 4.3.4 + eciesjs: 0.4.18 + eth-rpc-errors: 4.0.3 + eventemitter2: 6.4.9 + obj-multiplex: 1.0.0 + pump: 3.0.4 + readable-stream: 3.6.2 + socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + tslib: 2.8.1 + util: 0.12.5 + uuid: 8.3.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + + '@metamask/superstruct@3.2.1': {} + + '@metamask/utils@11.11.0': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@metamask/superstruct': 3.2.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@types/debug': 4.1.13 + '@types/lodash': 4.17.24 + debug: 4.4.3(supports-color@5.5.0) + lodash: 4.18.1 + pony-cause: 2.1.11 + semver: 7.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + '@metamask/utils@5.0.2': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@types/debug': 4.1.13 + debug: 4.4.3(supports-color@5.5.0) + semver: 7.8.1 + superstruct: 1.0.4 + transitivePeerDependencies: + - supports-color + + '@metamask/utils@8.5.0': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@metamask/superstruct': 3.2.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@types/debug': 4.1.13 + debug: 4.3.4 + pony-cause: 2.1.11 + semver: 7.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + '@metamask/utils@9.3.0': + dependencies: + '@ethereumjs/tx': 4.2.0 + '@metamask/superstruct': 3.2.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@types/debug': 4.1.13 + debug: 4.3.4 + pony-cause: 2.1.11 + semver: 7.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + '@motionone/animation@10.18.0': + dependencies: + '@motionone/easing': 10.18.0 + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/dom@10.12.0': + dependencies: + '@motionone/animation': 10.18.0 + '@motionone/generators': 10.18.0 + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + hey-listen: 1.0.8 + tslib: 2.8.1 + + '@motionone/easing@10.18.0': + dependencies: + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/generators@10.18.0': + dependencies: + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/types@10.17.1': {} + + '@motionone/utils@10.18.0': + dependencies: + '@motionone/types': 10.17.1 + hey-listen: 1.0.8 + tslib: 2.8.1 + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@noble/ciphers@1.2.1': {} + + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + + '@noble/curves@1.4.2': + dependencies: + '@noble/hashes': 1.4.0 + + '@noble/curves@1.8.0': + dependencies: + '@noble/hashes': 1.7.0 + + '@noble/curves@1.8.1': + dependencies: + '@noble/hashes': 1.7.1 + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.3.2': {} + + '@noble/hashes@1.4.0': {} + + '@noble/hashes@1.7.0': {} + + '@noble/hashes@1.7.1': {} + + '@noble/hashes@1.8.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@noir-lang/acvm_js@1.0.0-beta.16': {} + + '@noir-lang/noir_js@1.0.0-beta.16': + dependencies: + '@noir-lang/acvm_js': 1.0.0-beta.16 + '@noir-lang/noirc_abi': 1.0.0-beta.16 + '@noir-lang/types': 1.0.0-beta.16 + pako: 2.1.0 + + '@noir-lang/noirc_abi@1.0.0-beta.16': + dependencies: + '@noir-lang/types': 1.0.0-beta.16 + + '@noir-lang/types@1.0.0-beta.16': {} + + '@paulmillr/qr@0.2.1': {} + + '@phosphor-icons/react@2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@remix-run/router@1.23.2': {} + + '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + big.js: 6.2.2 + dayjs: 1.11.13 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@reown/appkit-controllers@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-pay@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76) + lit: 3.3.0 + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-polyfills@1.7.8': + dependencies: + buffer: 6.0.3 + + '@reown/appkit-scaffold-ui@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + lit: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - valtio + - zod + + '@reown/appkit-ui@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + lit: 3.3.0 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-utils@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/logger': 2.1.2 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@reown/appkit-wallet@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) + '@reown/appkit-polyfills': 1.7.8 + '@walletconnect/logger': 2.1.2 + zod: 3.22.4 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + + '@reown/appkit@1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-polyfills': 1.7.8 + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.21.0 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + bs58: 6.0.0 + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/plugin-inject@5.0.5(rollup@4.60.4)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.60.4) + estree-walker: 2.0.2 + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.60.4 + + '@rollup/plugin-virtual@3.0.2(rollup@4.60.4)': + optionalDependencies: + rollup: 4.60.4 + + '@rollup/pluginutils@5.3.0(rollup@4.60.4)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.4 + optionalDependencies: + rollup: 4.60.4 + + '@rollup/rollup-android-arm-eabi@4.60.4': + optional: true + + '@rollup/rollup-android-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-x64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.4': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.4': + optional: true + + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@safe-global/safe-gateway-typescript-sdk': 3.23.1 + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + + '@safe-global/safe-gateway-typescript-sdk@3.23.1': {} + + '@scure/base@1.1.9': {} + + '@scure/base@1.2.6': {} + + '@scure/bip32@1.4.0': + dependencies: + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.9 + + '@scure/bip32@1.6.2': + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/base': 1.2.6 + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@1.3.0': + dependencies: + '@noble/hashes': 1.4.0 + '@scure/base': 1.1.9 + + '@scure/bip39@1.5.4': + dependencies: + '@noble/hashes': 1.7.1 + '@scure/base': 1.2.6 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@socket.io/component-emitter@3.1.2': {} + + '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + + '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + + '@solana/accounts@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/addresses@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/assertions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-core@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-data-structures@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-numbers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs-strings@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/codecs@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/options': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/errors@5.5.1(typescript@5.9.3)': + dependencies: + chalk: 5.6.2 + commander: 14.0.2 + optionalDependencies: + typescript: 5.9.3 + + '@solana/fast-stable-stringify@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/functional@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/instruction-plans@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/instructions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/keys@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/assertions': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instruction-plans': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/plugin-core': 5.5.1(typescript@5.9.3) + '@solana/programs': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/signers': 5.5.1(typescript@5.9.3) + '@solana/sysvars': 5.5.1(typescript@5.9.3) + '@solana/transaction-confirmation': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/nominal-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/offchain-messages@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/options@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/plugin-core@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/programs@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/promises@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-api@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-parsed-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-parsed-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-spec-types@5.5.1(typescript@5.9.3)': + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-spec@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-subscriptions-api@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-subscriptions-channel-websocket@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@solana/rpc-subscriptions-spec@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-subscriptions@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions-channel-websocket': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/subscribable': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/rpc-transformers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc-transport-http@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + undici-types: 7.25.0 + optionalDependencies: + typescript: 5.9.3 + + '@solana/rpc-types@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/rpc@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/fast-stable-stringify': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/rpc-api': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec': 5.5.1(typescript@5.9.3) + '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-transformers': 5.5.1(typescript@5.9.3) + '@solana/rpc-transport-http': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/signers@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/offchain-messages': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/subscribable@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/errors': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + '@solana/sysvars@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/accounts': 5.5.1(typescript@5.9.3) + '@solana/codecs': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transaction-confirmation@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/promises': 5.5.1(typescript@5.9.3) + '@solana/rpc': 5.5.1(typescript@5.9.3) + '@solana/rpc-subscriptions': 5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + '@solana/transactions': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - fastestsmallesttextencoderdecoder + - utf-8-validate + + '@solana/transaction-messages@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/transactions@5.5.1(typescript@5.9.3)': + dependencies: + '@solana/addresses': 5.5.1(typescript@5.9.3) + '@solana/codecs-core': 5.5.1(typescript@5.9.3) + '@solana/codecs-data-structures': 5.5.1(typescript@5.9.3) + '@solana/codecs-numbers': 5.5.1(typescript@5.9.3) + '@solana/codecs-strings': 5.5.1(typescript@5.9.3) + '@solana/errors': 5.5.1(typescript@5.9.3) + '@solana/functional': 5.5.1(typescript@5.9.3) + '@solana/instructions': 5.5.1(typescript@5.9.3) + '@solana/keys': 5.5.1(typescript@5.9.3) + '@solana/nominal-types': 5.5.1(typescript@5.9.3) + '@solana/rpc-types': 5.5.1(typescript@5.9.3) + '@solana/transaction-messages': 5.5.1(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + + '@svgr/babel-preset@8.1.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.0) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.0) + + '@svgr/core@8.1.0(typescript@5.9.3)': + dependencies: + '@babel/core': 7.29.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.9.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.29.0 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': + dependencies: + '@babel/core': 7.29.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@svgr/core': 8.1.0(typescript@5.9.3) + cosmiconfig: 8.3.6(typescript@5.9.3) + deepmerge: 4.3.1 + svgo: 3.3.3 + transitivePeerDependencies: + - typescript + + '@svgr/rollup@8.1.0(rollup@4.60.4)(typescript@5.9.3)': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.29.0) + '@babel/preset-env': 7.29.5(@babel/core@7.29.0) + '@babel/preset-react': 7.28.5(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@rollup/pluginutils': 5.3.0(rollup@4.60.4) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + + '@swc/core-darwin-arm64@1.15.40': + optional: true + + '@swc/core-darwin-x64@1.15.40': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.40': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.40': + optional: true + + '@swc/core-linux-arm64-musl@1.15.40': + optional: true + + '@swc/core-linux-ppc64-gnu@1.15.40': + optional: true + + '@swc/core-linux-s390x-gnu@1.15.40': + optional: true + + '@swc/core-linux-x64-gnu@1.15.40': + optional: true + + '@swc/core-linux-x64-musl@1.15.40': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.40': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.40': + optional: true + + '@swc/core-win32-x64-msvc@1.15.40': + optional: true + + '@swc/core@1.15.40': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.26 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.40 + '@swc/core-darwin-x64': 1.15.40 + '@swc/core-linux-arm-gnueabihf': 1.15.40 + '@swc/core-linux-arm64-gnu': 1.15.40 + '@swc/core-linux-arm64-musl': 1.15.40 + '@swc/core-linux-ppc64-gnu': 1.15.40 + '@swc/core-linux-s390x-gnu': 1.15.40 + '@swc/core-linux-x64-gnu': 1.15.40 + '@swc/core-linux-x64-musl': 1.15.40 + '@swc/core-win32-arm64-msvc': 1.15.40 + '@swc/core-win32-ia32-msvc': 1.15.40 + '@swc/core-win32-x64-msvc': 1.15.40 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.26': + dependencies: + '@swc/counter': 0.1.3 + + '@swc/wasm@1.15.40': {} + + '@tailwindcss/typography@0.5.19(tailwindcss@3.4.19)': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.19 + + '@tanstack/query-core@5.100.14': {} + + '@tanstack/react-query@5.100.14(react@18.3.1)': + dependencies: + '@tanstack/query-core': 5.100.14 + react: 18.3.1 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.8': {} + + '@types/estree@1.0.9': {} + + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.11 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/lodash@4.17.24': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/node@22.7.5': + dependencies: + undici-types: 6.19.8 + + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.3.7(@types/react@18.3.29)': + dependencies: + '@types/react': 18.3.29 + + '@types/react-syntax-highlighter@15.5.13': + dependencies: + '@types/react': 18.3.29 + + '@types/react@18.3.29': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@types/trusted-types@2.0.7': {} + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@ungap/structured-clone@1.3.1': {} + + '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.7.5))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - supports-color + + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': + dependencies: + '@base-org/account': 2.4.0(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@coinbase/wallet-sdk': 4.3.6(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@gemini-wallet/core': 0.3.2(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@metamask/sdk': 0.33.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + cbw-sdk: '@coinbase/wallet-sdk@3.9.3' + porto: 0.2.35(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/react-query' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - react-native + - supports-color + - uploadthing + - use-sync-external-store + - utf-8-validate + - wagmi + - zod + + '@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.9.3) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.0(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + optionalDependencies: + '@tanstack/query-core': 5.100.14 + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + + '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.33.0 + events: 3.3.0 + uint8arrays: 3.1.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/environment@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/ethereum-provider@2.21.1(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@reown/appkit': 1.7.8(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - react + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/events@1.0.1': + dependencies: + keyvaluestorage-interface: 1.0.0 + tslib: 1.14.1 + + '@walletconnect/heartbeat@1.2.2': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/time': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-http-connection@1.0.8': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + cross-fetch: 3.2.0 + events: 3.3.0 + transitivePeerDependencies: + - encoding + + '@walletconnect/jsonrpc-provider@1.0.14': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-types@1.0.4': + dependencies: + events: 3.3.0 + keyvaluestorage-interface: 1.0.0 + + '@walletconnect/jsonrpc-utils@1.0.8': + dependencies: + '@walletconnect/environment': 1.0.1 + '@walletconnect/jsonrpc-types': 1.0.4 + tslib: 1.14.1 + + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.11(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@walletconnect/keyvaluestorage@1.1.1': + dependencies: + '@walletconnect/safe-json': 1.0.2 + idb-keyval: 6.2.4 + unstorage: 1.17.5(idb-keyval@6.2.4) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/logger@2.1.2': + dependencies: + '@walletconnect/safe-json': 1.0.2 + pino: 7.11.0 + + '@walletconnect/relay-api@1.0.11': + dependencies: + '@walletconnect/jsonrpc-types': 1.0.4 + + '@walletconnect/relay-auth@1.1.0': + dependencies: + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + uint8arrays: 3.1.0 + + '@walletconnect/safe-json@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 2.1.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/time@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/types@2.21.0': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/types@2.21.1': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/jsonrpc-http-connection': 1.0.8 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 2.1.2 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + es-toolkit: 1.33.0 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - encoding + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.0 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@noble/ciphers': 1.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.21.1 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + bs58: 6.0.0 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: 3.1.0 + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/window-getters@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/window-metadata@1.0.1': + dependencies: + '@walletconnect/window-getters': 1.0.1 + tslib: 1.14.1 + + '@zk-kit/lean-imt@2.2.4(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + dependencies: + '@zk-kit/utils': 1.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@zk-kit/utils@1.4.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + dependencies: + buffer: 6.0.3 + ethers: 6.13.5(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + abitype@1.0.6(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.0.8(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.1.0(typescript@5.9.3)(zod@3.22.4): + optionalDependencies: + typescript: 5.9.3 + zod: 3.22.4 + + abitype@1.1.0(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.2.3(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.2.4(typescript@5.9.3)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.3 + zod: 3.25.76 + + abitype@1.2.4(typescript@5.9.3)(zod@4.4.3): + optionalDependencies: + typescript: 5.9.3 + zod: 4.4.3 + + add@2.0.6: {} + + aes-js@4.0.0-beta.5: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + array-union@2.1.0: {} + + asn1.js@4.10.1: + dependencies: + bn.js: 4.12.3 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + assert@2.1.0: + dependencies: + call-bind: 1.0.9 + is-nan: 1.3.2 + object-is: 1.1.6 + object.assign: 4.1.7 + util: 0.12.5 + + async-mutex@0.2.6: + dependencies: + tslib: 2.8.1 + + async@3.2.6: {} + + asynckit@0.4.0: {} + + atomic-sleep@1.0.0: {} + + autoprefixer@10.5.0(postcss@8.5.15): + dependencies: + browserslist: 4.28.2 + caniuse-lite: 1.0.30001793 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios-retry@4.5.0(axios@1.16.0): + dependencies: + axios: 1.16.0 + is-retry-allowed: 2.2.0 + + axios@1.13.2: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axios@1.16.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.29.2 + cosmiconfig: 7.1.0 + resolve: 1.22.12 + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + babel-plugin-styled-components@2.3.0(@babel/core@7.29.0)(styled-components@5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1))(supports-color@5.5.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + picomatch: 4.0.4 + styled-components: 5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1) + transitivePeerDependencies: + - supports-color + + bail@2.0.2: {} + + base-x@5.0.1: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.10.32: {} + + big.js@6.2.2: {} + + binary-extensions@2.3.0: {} + + bn.js@4.12.3: {} + + bn.js@5.2.3: {} + + boolbase@1.0.0: {} + + bowser@2.14.1: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + brorand@1.1.0: {} + + browser-resolve@2.0.0: + dependencies: + resolve: 1.22.12 + + browserify-aes@1.2.0: + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.7 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-cipher@1.0.1: + dependencies: + browserify-aes: 1.2.0 + browserify-des: 1.0.2 + evp_bytestokey: 1.0.3 + + browserify-des@1.0.2: + dependencies: + cipher-base: 1.0.7 + des.js: 1.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-rsa@4.1.1: + dependencies: + bn.js: 5.2.3 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + browserify-sign@4.2.5: + dependencies: + bn.js: 5.2.3 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + create-hmac: 1.1.7 + elliptic: 6.6.1 + inherits: 2.0.4 + parse-asn1: 5.1.9 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.32 + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.361 + node-releases: 2.0.46 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + bs58@6.0.0: + dependencies: + base-x: 5.0.1 + + buffer-xor@1.0.3: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bufferutil@4.1.0: + dependencies: + node-gyp-build: 4.8.4 + + builtin-status-codes@3.0.0: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase-css@2.0.1: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + camelize@1.0.1: {} + + caniuse-lite@1.0.30001793: {} + + ccount@2.0.1: {} + + chalk@5.6.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@1.1.4: {} + + character-entities-legacy@3.0.0: {} + + character-entities@1.2.4: {} + + character-entities@2.0.2: {} + + character-reference-invalid@1.1.4: {} + + character-reference-invalid@2.0.1: {} + + charenc@0.0.2: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + cipher-base@1.0.7: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + clsx@1.2.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comlink@4.4.2: {} + + comma-separated-tokens@1.0.8: {} + + comma-separated-tokens@2.0.3: {} + + commander@12.1.0: {} + + commander@13.1.0: {} + + commander@14.0.2: {} + + commander@4.1.1: {} + + commander@7.2.0: {} + + commondir@1.0.1: {} + + connectkit@1.9.2(@babel/core@7.29.0)(@tanstack/react-query@5.100.14(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): + dependencies: + '@aave/account': 0.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + '@tanstack/react-query': 5.100.14(react@18.3.1) + buffer: 6.0.3 + detect-browser: 5.3.0 + framer-motion: 6.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + qrcode: 1.5.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-state: 1.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-use-measure: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + resize-observer-polyfill: 1.5.1 + styled-components: 5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + transitivePeerDependencies: + - '@babel/core' + - react-is + + console-browserify@1.2.0: {} + + constants-browserify@1.0.0: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie-es@1.2.3: {} + + core-js-compat@3.49.0: + dependencies: + browserslist: 4.28.2 + + core-util-is@1.0.3: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + + crc-32@1.2.2: {} + + create-ecdh@4.0.4: + dependencies: + bn.js: 4.12.3 + elliptic: 6.6.1 + + create-hash@1.2.0: + dependencies: + cipher-base: 1.0.7 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.3 + sha.js: 2.4.12 + + create-hmac@1.1.7: + dependencies: + cipher-base: 1.0.7 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.3 + safe-buffer: 5.2.1 + sha.js: 2.4.12 + + create-require@1.1.1: {} + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-fetch@4.1.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + crypt@0.0.2: {} + + crypto-browserify@3.12.1: + dependencies: + browserify-cipher: 1.0.1 + browserify-sign: 4.2.5 + create-ecdh: 4.0.4 + create-hash: 1.2.0 + create-hmac: 1.1.7 + diffie-hellman: 5.0.3 + hash-base: 3.0.5 + inherits: 2.0.4 + pbkdf2: 3.1.5 + public-encrypt: 4.0.3 + randombytes: 2.1.0 + randomfill: 1.0.4 + + css-color-keywords@1.0.0: {} + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-to-react-native@3.2.0: + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + cssesc@3.0.0: {} + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + csstype@3.2.3: {} + + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.29.2 + + dayjs@1.11.13: {} + + debug@4.3.4: + dependencies: + ms: 2.1.2 + + debug@4.4.3(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + decamelize@1.2.0: {} + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + decode-uri-component@0.2.2: {} + + deepmerge@4.3.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + defu@6.1.7: {} + + delayed-stream@1.0.0: {} + + dequal@2.0.3: {} + + derive-valtio@0.1.0(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1)): + dependencies: + valtio: 1.13.2(@types/react@18.3.29)(react@18.3.1) + + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + destr@2.0.5: {} + + detect-browser@5.3.0: {} + + detect-libc@2.1.2: + optional: true + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + didyoumean@1.2.2: {} + + diffie-hellman@5.0.3: + dependencies: + bn.js: 4.12.3 + miller-rabin: 4.0.1 + randombytes: 2.1.0 + + dijkstrajs@1.0.3: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domain-browser@4.22.0: {} + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexify@4.1.3: + dependencies: + end-of-stream: 1.4.5 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + + eciesjs@0.4.18: + dependencies: + '@ecies/ciphers': 0.2.6(@noble/ciphers@1.3.0) + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + + electron-to-chromium@1.5.361: {} + + elliptic@6.6.1: + dependencies: + bn.js: 4.12.3 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + email-addresses@5.0.0: {} + + emoji-regex@8.0.0: {} + + encode-utf8@1.0.3: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + engine.io-client@6.6.5(bufferutil@4.1.0)(utf-8-validate@5.0.10): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3(supports-color@5.5.0) + engine.io-parser: 5.2.3 + ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + entities@4.5.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + es-toolkit@1.33.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + estree-util-is-identifier-name@3.0.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + eth-block-tracker@7.1.0: + dependencies: + '@metamask/eth-json-rpc-provider': 1.0.1 + '@metamask/safe-event-emitter': 3.1.2 + '@metamask/utils': 5.0.2 + json-rpc-random-id: 1.0.1 + pify: 3.0.0 + transitivePeerDependencies: + - supports-color + + eth-json-rpc-filters@6.0.1: + dependencies: + '@metamask/safe-event-emitter': 3.1.2 + async-mutex: 0.2.6 + eth-query: 2.1.2 + json-rpc-engine: 6.1.0 + pify: 5.0.0 + + eth-query@2.1.2: + dependencies: + json-rpc-random-id: 1.0.1 + xtend: 4.0.2 + + eth-rpc-errors@4.0.3: + dependencies: + fast-safe-stringify: 2.1.1 + + ethereum-cryptography@2.2.1: + dependencies: + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@scure/bip32': 1.4.0 + '@scure/bip39': 1.3.0 + + ethers@6.13.5(bufferutil@4.1.0)(utf-8-validate@5.0.10): + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 22.7.5 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 22.7.5 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + eventemitter2@6.4.9: {} + + eventemitter3@5.0.1: {} + + eventemitter3@5.0.4: {} + + events@3.3.0: {} + + evp_bytestokey@1.0.3: + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + + extend@3.0.2: {} + + extension-port-stream@3.0.0: + dependencies: + readable-stream: 3.6.2 + webextension-polyfill: 0.10.0 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-redact@3.5.0: {} + + fast-safe-stringify@2.1.1: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fault@1.0.4: + dependencies: + format: 0.2.2 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + filename-reserved-regex@2.0.0: {} + + filenamify@4.3.0: + dependencies: + filename-reserved-regex: 2.0.0 + strip-outer: 1.0.1 + trim-repeated: 1.0.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + filter-obj@1.1.0: {} + + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + + find-root@1.1.0: {} + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + follow-redirects@1.16.0: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.3 + mime-types: 2.1.35 + + format@0.2.2: {} + + fraction.js@5.3.4: {} + + framer-motion@6.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@motionone/dom': 10.12.0 + framesync: 6.0.1 + hey-listen: 1.0.8 + popmotion: 11.0.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + style-value-types: 5.0.0 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 0.8.8 + + framesync@6.0.1: + dependencies: + tslib: 2.8.1 + + fs-extra@11.3.5: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + gh-pages@6.3.0: + dependencies: + async: 3.2.6 + commander: 13.1.0 + email-addresses: 5.0.0 + filenamify: 4.3.0 + find-cache-dir: 3.3.2 + fs-extra: 11.3.5 + globby: 11.1.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globrex@0.1.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.4 + uncrypto: 0.1.3 + + has-flag@3.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hash-base@3.0.5: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + hash-base@3.1.2: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + hasown@2.0.3: + dependencies: + function-bind: 1.1.2 + + hast-util-parse-selector@2.2.5: {} + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + + hey-listen@1.0.8: {} + + highlight.js@10.7.3: {} + + highlightjs-vue@1.0.0: {} + + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hono@4.12.22: {} + + html-url-attributes@3.0.1: {} + + https-browserify@1.0.0: {} + + idb-keyval@6.2.1: {} + + idb-keyval@6.2.4: {} + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + inherits@2.0.4: {} + + inline-style-parser@0.2.7: {} + + iron-webcrypto@1.2.1: {} + + is-alphabetical@1.0.4: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@1.0.4: + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-arrayish@0.2.1: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-buffer@1.1.6: {} + + is-callable@1.2.7: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.3 + + is-decimal@1.0.4: {} + + is-decimal@2.0.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@1.0.4: {} + + is-hexadecimal@2.0.1: {} + + is-nan@1.3.2: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + + is-number@7.0.0: {} + + is-plain-obj@4.1.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + is-retry-allowed@2.2.0: {} + + is-stream@2.0.1: {} + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isomorphic-timers-promises@1.0.1: {} + + isows@1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + + isows@1.0.7(ws@8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) + + isows@1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + + isows@1.0.7(ws@8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + + jiti@1.21.7: {} + + jose@6.2.3: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json-rpc-engine@6.1.0: + dependencies: + '@metamask/safe-event-emitter': 2.0.0 + eth-rpc-errors: 4.0.3 + + json-rpc-random-id@1.0.1: {} + + json5@2.2.3: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keccak@3.0.4: + dependencies: + node-addon-api: 2.0.2 + node-gyp-build: 4.8.4 + readable-stream: 3.6.2 + + keyvaluestorage-interface@1.0.0: {} + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lit-element@4.2.2: + dependencies: + '@lit-labs/ssr-dom-shim': 1.6.0 + '@lit/reactive-element': 2.1.2 + lit-html: 3.3.3 + + lit-html@3.3.3: + dependencies: + '@types/trusted-types': 2.0.7 + + lit@3.3.0: + dependencies: + '@lit/reactive-element': 2.1.2 + lit-element: 4.2.2 + lit-html: 3.3.3 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.debounce@4.0.8: {} + + lodash@4.18.1: {} + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + + lru-cache@11.5.0: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + math-intrinsics@1.1.0: {} + + md5.js@1.3.5: + dependencies: + hash-base: 3.0.5 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + md5@2.3.0: + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: 1.1.6 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.1 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + merge2@1.4.1: {} + + micro-ftch@0.3.1: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3(supports-color@5.5.0) + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + miller-rabin@4.0.1: + dependencies: + bn.js: 4.12.3 + brorand: 1.1.0 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + + mipd@0.0.7(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + ms@2.1.2: {} + + ms@2.1.3: {} + + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.12: + optionalDependencies: + msgpackr-extract: 3.0.3 + + multiformats@9.9.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.12: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-addon-api@2.0.2: {} + + node-fetch-native@1.6.7: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.1.2 + optional: true + + node-gyp-build@4.8.4: {} + + node-mock-http@1.0.4: {} + + node-releases@2.0.46: {} + + node-stdlib-browser@1.3.1: + dependencies: + assert: 2.1.0 + browser-resolve: 2.0.0 + browserify-zlib: 0.2.0 + buffer: 5.7.1 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + create-require: 1.1.1 + crypto-browserify: 3.12.1 + domain-browser: 4.22.0 + events: 3.3.0 + https-browserify: 1.0.0 + isomorphic-timers-promises: 1.0.1 + os-browserify: 0.3.0 + path-browserify: 1.0.1 + pkg-dir: 5.0.0 + process: 0.11.10 + punycode: 1.4.1 + querystring-es3: 0.2.1 + readable-stream: 3.6.2 + stream-browserify: 3.0.0 + stream-http: 3.2.0 + string_decoder: 1.3.0 + timers-browserify: 2.0.12 + tty-browserify: 0.0.1 + url: 0.11.4 + util: 0.12.5 + vm-browserify: 1.1.2 + + normalize-path@3.0.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + obj-multiplex@1.0.0: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + readable-stream: 2.3.8 + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + object-inspect@1.13.4: {} + + object-is@1.1.6: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.4 + + on-exit-leak-free@0.2.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + openapi-fetch@0.13.8: + dependencies: + openapi-typescript-helpers: 0.0.15 + + openapi-typescript-helpers@0.0.15: {} + + os-browserify@0.3.0: {} + + ox@0.14.22(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.6.7(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.6.9(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.4(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.7.1(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.9.17(typescript@5.9.3)(zod@4.4.3): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.4(typescript@5.9.3)(zod@4.4.3) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.9.6(typescript@5.9.3)(zod@3.22.4): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.9.3)(zod@3.22.4) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.9.6(typescript@5.9.3)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.9.3)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-try@2.2.0: {} + + pako@1.0.11: {} + + pako@2.1.0: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-asn1@5.1.9: + dependencies: + asn1.js: 4.10.1 + browserify-aes: 1.2.0 + evp_bytestokey: 1.0.3 + pbkdf2: 3.1.5 + safe-buffer: 5.2.1 + + parse-entities@2.0.0: + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + pbkdf2@3.1.5: + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.3 + safe-buffer: 5.2.1 + sha.js: 2.4.12 + to-buffer: 1.2.2 + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pify@2.3.0: {} + + pify@3.0.0: {} + + pify@5.0.0: {} + + pino-abstract-transport@0.5.0: + dependencies: + duplexify: 4.1.3 + split2: 4.2.0 + + pino-std-serializers@4.0.0: {} + + pino@7.11.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 0.2.0 + pino-abstract-transport: 0.5.0 + pino-std-serializers: 4.0.0 + process-warning: 1.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.1.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 2.8.0 + thread-stream: 0.15.2 + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + pkg-dir@5.0.0: + dependencies: + find-up: 5.0.0 + + pngjs@5.0.0: {} + + pony-cause@2.1.11: {} + + popmotion@11.0.3: + dependencies: + framesync: 6.0.1 + hey-listen: 1.0.8 + style-value-types: 5.0.0 + tslib: 2.8.1 + + porto@0.2.35(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): + dependencies: + '@wagmi/core': 2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + hono: 4.12.22 + idb-keyval: 6.2.4 + mipd: 0.0.7(typescript@5.9.3) + ox: 0.9.17(typescript@5.9.3)(zod@4.4.3) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zod: 4.4.3 + zustand: 5.0.13(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)) + optionalDependencies: + '@tanstack/react-query': 5.100.14(react@18.3.1) + react: 18.3.1 + typescript: 5.9.3 + wagmi: 2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + transitivePeerDependencies: + - '@types/react' + - immer + - use-sync-external-store + + poseidon-lite@0.3.0: {} + + possible-typed-array-names@1.1.0: {} + + postcss-import@15.1.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.12 + + postcss-js@4.1.0(postcss@8.5.15): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.15 + + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.15): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 1.21.7 + postcss: 8.5.15 + + postcss-nested@6.2.0(postcss@8.5.15): + dependencies: + postcss: 8.5.15 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.24.2: {} + + preact@10.29.2: {} + + prettier-plugin-tailwindcss@0.5.14(prettier@3.8.3): + dependencies: + prettier: 3.8.3 + + prettier@3.8.3: {} + + prismjs@1.27.0: {} + + prismjs@1.30.0: {} + + process-nextick-args@2.0.1: {} + + process-warning@1.0.0: {} + + process@0.11.10: {} + + property-information@5.6.0: + dependencies: + xtend: 4.0.2 + + property-information@7.1.0: {} + + proxy-compare@2.6.0: {} + + proxy-from-env@1.1.0: {} + + proxy-from-env@2.1.0: {} + + public-encrypt@4.0.3: + dependencies: + bn.js: 4.12.3 + browserify-rsa: 4.1.1 + create-hash: 1.2.0 + parse-asn1: 5.1.9 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@1.4.1: {} + + qrcode@1.5.3: + dependencies: + dijkstrajs: 1.0.3 + encode-utf8: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + qs@6.15.2: + dependencies: + side-channel: 1.1.0 + + query-string@7.1.3: + dependencies: + decode-uri-component: 0.2.2 + filter-obj: 1.1.0 + split-on-first: 1.1.0 + strict-uri-encode: 2.0.0 + + querystring-es3@0.2.1: {} + + queue-microtask@1.2.3: {} + + quick-format-unescaped@4.0.4: {} + + radix3@1.1.2: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + randomfill@1.0.4: + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-is@16.13.1: {} + + react-markdown@9.1.0(@types/react@18.3.29)(react@18.3.1): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 18.3.29 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react-refresh@0.17.0: {} + + react-router-dom@6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@remix-run/router': 1.23.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.30.3(react@18.3.1) + + react-router@6.30.3(react@18.3.1): + dependencies: + '@remix-run/router': 1.23.2 + react: 18.3.1 + + react-syntax-highlighter@15.6.6(react@18.3.1): + dependencies: + '@babel/runtime': 7.29.2 + highlight.js: 10.7.3 + highlightjs-vue: 1.0.0 + lowlight: 1.20.0 + prismjs: 1.30.0 + react: 18.3.1 + refractor: 3.6.0 + + react-transition-state@1.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-use-measure@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + readdirp@5.0.0: {} + + real-require@0.1.0: {} + + refractor@3.6.0: + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.27.0 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.1 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.1: + dependencies: + jsesc: 3.1.0 + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + require-directory@2.1.1: {} + + require-main-filename@2.0.0: {} + + resize-observer-polyfill@1.5.1: {} + + resolve-from@4.0.0: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + ripemd160@2.0.3: + dependencies: + hash-base: 3.1.2 + inherits: 2.0.4 + + rollup@4.60.4: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safe-stable-stringify@2.5.0: {} + + sax@1.6.0: {} + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + semver@7.8.1: {} + + set-blocking@2.0.0: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + setimmediate@1.0.5: {} + + sha.js@2.4.12: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + to-buffer: 1.2.2 + + shallowequal@1.1.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + slash@3.0.0: {} + + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3(supports-color@5.5.0) + engine.io-client: 6.6.5(bufferutil@4.1.0)(utf-8-validate@5.0.10) + socket.io-parser: 4.2.6 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.6: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + sonic-boom@2.8.0: + dependencies: + atomic-sleep: 1.0.0 + + source-map-js@1.2.1: {} + + source-map@0.5.7: {} + + space-separated-tokens@1.1.5: {} + + space-separated-tokens@2.0.2: {} + + split-on-first@1.1.0: {} + + split2@4.2.0: {} + + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + + stream-http@3.2.0: + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + xtend: 4.0.2 + + stream-shift@1.0.3: {} + + strict-uri-encode@2.0.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-outer@1.0.1: + dependencies: + escape-string-regexp: 1.0.5 + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + style-value-types@5.0.0: + dependencies: + hey-listen: 1.0.8 + tslib: 2.8.1 + + styled-components@5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1): + dependencies: + '@babel/helper-module-imports': 7.28.6(supports-color@5.5.0) + '@babel/traverse': 7.29.0(supports-color@5.5.0) + '@emotion/is-prop-valid': 1.4.0 + '@emotion/stylis': 0.8.5 + '@emotion/unitless': 0.7.5 + babel-plugin-styled-components: 2.3.0(@babel/core@7.29.0)(styled-components@5.3.11(@babel/core@7.29.0)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1))(supports-color@5.5.0) + css-to-react-native: 3.2.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 16.13.1 + shallowequal: 1.1.0 + supports-color: 5.5.0 + transitivePeerDependencies: + - '@babel/core' + + stylis@4.2.0: {} + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.16 + ts-interface-checker: 0.1.13 + + superstruct@1.0.4: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svg-parser@2.0.4: {} + + svgo@3.3.3: + dependencies: + commander: 7.2.0 + css-select: 5.2.2 + css-tree: 2.3.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.0 + + tailwindcss@3.4.19: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.15 + postcss-import: 15.1.0(postcss@8.5.15) + postcss-js: 4.1.0(postcss@8.5.15) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.15) + postcss-nested: 6.2.0(postcss@8.5.15) + postcss-selector-parser: 6.1.2 + resolve: 1.22.12 + sucrase: 3.35.1 + transitivePeerDependencies: + - tsx + - yaml + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + thread-stream@0.15.2: + dependencies: + real-require: 0.1.0 + + timers-browserify@2.0.12: + dependencies: + setimmediate: 1.0.5 + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + to-buffer@1.2.2: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tr46@0.0.3: {} + + trim-lines@3.0.1: {} + + trim-repeated@1.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + trough@2.2.0: {} + + ts-interface-checker@0.1.13: {} + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + tslib@1.14.1: {} + + tslib@2.7.0: {} + + tslib@2.8.1: {} + + tty-browserify@0.0.1: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typescript@5.9.3: {} + + ufo@1.6.4: {} + + uint8arrays@3.1.0: + dependencies: + multiformats: 9.9.0 + + uncrypto@0.1.3: {} + + undici-types@6.19.8: {} + + undici-types@7.25.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + universalify@2.0.1: {} + + unstorage@1.17.5(idb-keyval@6.2.4): + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.11 + lru-cache: 11.5.0 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.4 + optionalDependencies: + idb-keyval: 6.2.4 + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + url@0.11.4: + dependencies: + punycode: 1.4.1 + qs: 6.15.2 + + use-sync-external-store@1.2.0(react@18.3.1): + dependencies: + react: 18.3.1 + + use-sync-external-store@1.4.0(react@18.3.1): + dependencies: + react: 18.3.1 + + utf-8-validate@5.0.10: + dependencies: + node-gyp-build: 4.8.4 + + util-deprecate@1.0.2: {} + + util@0.12.5: + dependencies: + inherits: 2.0.4 + is-arguments: 1.2.0 + is-generator-function: 1.1.2 + is-typed-array: 1.1.15 + which-typed-array: 1.1.20 + + uuid@10.0.0: {} + + uuid@8.3.2: {} + + uuid@9.0.1: {} + + valtio@1.13.2(@types/react@18.3.29)(react@18.3.1): + dependencies: + derive-valtio: 0.1.0(valtio@1.13.2(@types/react@18.3.29)(react@18.3.1)) + proxy-compare: 2.6.0 + use-sync-external-store: 1.2.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.29 + react: 18.3.1 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + viem@2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.6.7(typescript@5.9.3)(zod@3.25.76) + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.30.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.7(ws@8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.7.1(typescript@5.9.3)(zod@3.25.76) + ws: 8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.9.3)(zod@3.22.4) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.9.6(typescript@5.9.3)(zod@3.22.4) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.9.6(typescript@5.9.3)(zod@3.25.76) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.50.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + isows: 1.0.7(ws@8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.14.22(typescript@5.9.3)(zod@3.25.76) + ws: 8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + vite-plugin-node-polyfills@0.22.0(rollup@4.60.4)(vite@5.4.21(@types/node@22.7.5)): + dependencies: + '@rollup/plugin-inject': 5.0.5(rollup@4.60.4) + node-stdlib-browser: 1.3.1 + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - rollup + + vite-plugin-top-level-await@1.6.0(rollup@4.60.4)(vite@5.4.21(@types/node@22.7.5)): + dependencies: + '@rollup/plugin-virtual': 3.0.2(rollup@4.60.4) + '@swc/core': 1.15.40 + '@swc/wasm': 1.15.40 + uuid: 10.0.0 + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - '@swc/helpers' + - rollup + + vite-tsconfig-paths@4.3.2(typescript@5.9.3)(vite@5.4.21(@types/node@22.7.5)): + dependencies: + debug: 4.4.3(supports-color@5.5.0) + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + optionalDependencies: + vite: 5.4.21(@types/node@22.7.5) + transitivePeerDependencies: + - supports-color + - typescript + + vite@5.4.21(@types/node@22.7.5): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.15 + rollup: 4.60.4 + optionalDependencies: + '@types/node': 22.7.5 + fsevents: 2.3.3 + + vm-browserify@1.1.2: {} + + wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): + dependencies: + '@tanstack/react-query': 5.100.14(react@18.3.1) + '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.100.14)(@tanstack/react-query@5.100.14(react@18.3.1))(@types/react@18.3.29)(bufferutil@4.1.0)(react@18.3.1)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.100.14)(@types/react@18.3.29)(react@18.3.1)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@18.3.1))(viem@2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + viem: 2.38.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/query-core' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react-native + - supports-color + - uploadthing + - utf-8-validate + - zod + + webextension-polyfill@0.10.0: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-module@2.0.1: {} + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + ws@7.5.11(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.18.2(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.20.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + xmlhttprequest-ssl@2.1.2: {} + + xtend@4.0.2: {} + + y18n@4.0.3: {} + + yallist@3.1.1: {} + + yaml@1.10.3: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yocto-queue@0.1.0: {} + + zod@3.22.4: {} + + zod@3.25.76: {} + + zod@4.4.3: {} + + zustand@5.0.0(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)): + optionalDependencies: + '@types/react': 18.3.29 + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + + zustand@5.0.13(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)): + optionalDependencies: + '@types/react': 18.3.29 + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + + zustand@5.0.3(@types/react@18.3.29)(react@18.3.1)(use-sync-external-store@1.4.0(react@18.3.1)): + optionalDependencies: + '@types/react': 18.3.29 + react: 18.3.1 + use-sync-external-store: 1.4.0(react@18.3.1) + + zwitch@2.0.4: {} diff --git a/examples/CRISP/client/src/providers/Web3Provider.tsx b/examples/CRISP/client/src/providers/Web3Provider.tsx index 3a54781f1..af002e728 100644 --- a/examples/CRISP/client/src/providers/Web3Provider.tsx +++ b/examples/CRISP/client/src/providers/Web3Provider.tsx @@ -18,7 +18,6 @@ if (!walletConnectProjectId) const config = createConfig( getDefaultConfig({ appName: 'CRISP', - enableFamily: false, chains: [getChain()], transports: { [anvil.id]: http(anvil.rpcUrls.default.http[0]), diff --git a/examples/CRISP/client/src/utils/methods.ts b/examples/CRISP/client/src/utils/methods.ts index 8184d1fe3..12974640f 100644 --- a/examples/CRISP/client/src/utils/methods.ts +++ b/examples/CRISP/client/src/utils/methods.ts @@ -50,13 +50,13 @@ export const convertPollData = (request: PollRequestResult[]): PollResult[] => { const options: PollOption[] = [ { value: 0, - votes: Number.parseInt(poll.tally[0] ?? '0', 10) || 0, + votes: Number.parseInt(String(poll.tally[0] ?? '0'), 10) || 0, label: poll.option_1_emoji, checked: false, }, { value: 1, - votes: Number.parseInt(poll.tally[1] ?? '0', 10) || 0, + votes: Number.parseInt(String(poll.tally[1] ?? '0'), 10) || 0, label: poll.option_2_emoji, checked: false, }, diff --git a/examples/CRISP/client/vercel.json b/examples/CRISP/client/vercel.json index e2cbdd562..229370f34 100644 --- a/examples/CRISP/client/vercel.json +++ b/examples/CRISP/client/vercel.json @@ -1,5 +1,9 @@ { - "installCommand": "pnpm install --no-link-workspace-packages --no-lockfile", + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "vite", + "installCommand": "corepack enable && corepack prepare pnpm@10.7.1 --activate && pnpm install --ignore-workspace", + "buildCommand": "pnpm run build", + "outputDirectory": "dist", "headers": [ { "source": "/(.*)", From 742234326b926fd634d8b6aee5c511fabaff0233 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sat, 23 May 2026 19:58:56 +0200 Subject: [PATCH 45/54] avoid leak env --- crates/tests/tests/integration.rs | 36 +++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index 7dfbdc57f..85c7bdf7a 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -208,6 +208,30 @@ impl Drop for EnvTimeoutVarsGuard { } } +/// RAII guard that restores a single env var on scope exit. +struct ScopedEnvVar { + name: &'static str, + original: Option, +} + +impl ScopedEnvVar { + fn set(name: &'static str, value: &str) -> Self { + let original = std::env::var_os(name); + std::env::set_var(name, value); + Self { name, original } + } +} + +impl Drop for ScopedEnvVar { + fn drop(&mut self) { + if let Some(v) = &self.original { + std::env::set_var(self.name, v); + } else { + std::env::remove_var(self.name); + } + } +} + async fn copy_dir_recursive(src: &std::path::Path, dst: &std::path::Path) -> Result<()> { tokio::fs::create_dir_all(dst).await?; let mut entries = tokio::fs::read_dir(src).await?; @@ -1159,15 +1183,15 @@ async fn test_trbfv_actor() -> Result<()> { // Actor system setup let concurrent_jobs = benchmark_multithread_concurrent_jobs(); - if benchmark_proof_aggregation_enabled() - && std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER").is_err() - { + let _fold_verifier_env_guard = (benchmark_proof_aggregation_enabled() + && std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER").is_err()) + .then(|| { // In-process benchmark has no RPC; same default as pre-registry-fetch harness. - std::env::set_var( + ScopedEnvVar::set( "BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER", "0x7969c5eD335650692Bc04293B07F5BF2e7A673C0", - ); - } + ) + }); let slashing_manager_addr = benchmark_slashing_manager_address(); let max_threadroom = Multithread::get_max_threads_minus(1); let pool_threads = concurrent_jobs.min(max_threadroom).max(1); From 07b90a30bea09792a8b3a7f053e427dd78958922 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sat, 23 May 2026 23:22:20 +0200 Subject: [PATCH 46/54] fix e2e crisp ci --- examples/CRISP/test/crisp.spec.ts | 37 +++++++++++++++---- .../CRISP/test/wallet-setup/basic.setup.ts | 2 +- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index eeb4dc9be..d0bf3d407 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -86,6 +86,32 @@ function log(msg: string) { // ConnectKit modal animations + app initialization (initialLoad/switchChain) // can cause the MetaMask button to be detached from the DOM or the page to // navigate while the modal is opening. Retry the whole flow up to 3 times. +// After reload, wagmi/ConnectKit reconnect and App.tsx switchChain can lag on CI. +// Wait until the demo poll is interactive and the wallet session is restored. +async function waitForDemoPollReady(page: Page) { + await page.waitForLoadState('load') + await expect(page.locator("[data-test-id='poll-button-0']")).toBeVisible({ timeout: 60_000 }) + await expect(page.locator('button:has-text("Connect Wallet")')).not.toBeVisible({ timeout: 60_000 }) + await expect(page.locator('.tag.live')).toBeVisible({ timeout: 60_000 }) +} + +async function castVoteWithSignature(page: Page, metamask: MetaMask) { + log(`clicking first vote card...`) + await page.locator("[data-test-id='poll-button-0']").click() + + const castBtn = page.locator('button:has-text("Cast")') + await expect(castBtn).toBeEnabled({ timeout: 30_000 }) + + log(`clicking Cast Vote...`) + await castBtn.click() + + log(`waiting for wallet signing prompt...`) + await expect(page.getByText('Please sign the message in your wallet')).toBeVisible({ timeout: 30_000 }) + + log(`confirming MetaMask signature request...`) + await metamask.confirmSignature() +} + async function connectWalletWithRetry(page: Page, maxAttempts = 3) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { @@ -149,13 +175,10 @@ test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) => log(`DKG duration: ${DKG_DURATION}ms`) log(`forcing page reload...`) await page.reload() - - log(`clicking first vote card...`) - await page.locator("[data-test-id='poll-button-0']").click() - log(`clicking Cast Vote...`) - await page.locator('button:has-text("Cast")').click() - log(`confirming MetaMask signature request...`) - await metamask.confirmSignature() + log(`ensuring local anvil network after reload...`) + await metamask.switchNetwork('localwallet') + await waitForDemoPollReady(page) + await castVoteWithSignature(page, metamask) const WAIT = E3_DURATION - DKG_DURATION + OUTPUT_DECRYPTION_WAIT log(`waiting ${WAIT}ms...`) await page.waitForTimeout(WAIT) diff --git a/examples/CRISP/test/wallet-setup/basic.setup.ts b/examples/CRISP/test/wallet-setup/basic.setup.ts index eff851634..50525c947 100644 --- a/examples/CRISP/test/wallet-setup/basic.setup.ts +++ b/examples/CRISP/test/wallet-setup/basic.setup.ts @@ -22,5 +22,5 @@ export default defineWalletSetup(PASSWORD, async (context, walletPage) => { symbol: 'ETH', } await metamask.addNetwork(customNetwork) - // await metamask.switchNetwork("localwallet"); + await metamask.switchNetwork('localwallet') }) From 3efcf886582b57c57a0c30c452f8bcdb99fce461 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sat, 23 May 2026 23:41:26 +0200 Subject: [PATCH 47/54] increase timeout --- examples/CRISP/test/crisp.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index d0bf3d407..9d964af47 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -105,8 +105,9 @@ async function castVoteWithSignature(page: Page, metamask: MetaMask) { log(`clicking Cast Vote...`) await castBtn.click() + // useVoteCasting sets StepMessage with trailing "..."; getByText defaults to exact match log(`waiting for wallet signing prompt...`) - await expect(page.getByText('Please sign the message in your wallet')).toBeVisible({ timeout: 30_000 }) + await expect(page.getByText('Please sign the message in your wallet', { exact: false })).toBeVisible({ timeout: 60_000 }) log(`confirming MetaMask signature request...`) await metamask.confirmSignature() From 142034aeb7a095ff1199e91916686d5f67c3d97c Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sun, 24 May 2026 00:11:57 +0200 Subject: [PATCH 48/54] diagnostic for crisp e2e fail in ci --- examples/CRISP/client/.env.example | 2 + .../client/src/hooks/voting/useVoteCasting.ts | 35 +++++++- .../pages/Landing/components/DailyPoll.tsx | 15 ++++ .../client/src/providers/Web3Provider.tsx | 9 ++- examples/CRISP/client/src/utils/methods.ts | 2 + examples/CRISP/test/crisp.spec.ts | 80 +++++++++++++++---- 6 files changed, 122 insertions(+), 21 deletions(-) diff --git a/examples/CRISP/client/.env.example b/examples/CRISP/client/.env.example index 4108162a6..eee867acd 100644 --- a/examples/CRISP/client/.env.example +++ b/examples/CRISP/client/.env.example @@ -1,4 +1,6 @@ VITE_ENCLAVE_API=http://127.0.0.1:4000 +VITE_CHAIN_ID=31337 +VITE_RPC_URL=http://127.0.0.1:8545 VITE_WALLETCONNECT_PROJECT_ID= # Voting eligibility token (MockVotingToken). Updated automatically on `pnpm dev:up` deploy. # Enclave fee token (MockUSDC) is separate: packages/enclave-contracts/deployed_contracts.json → MockUSDC. diff --git a/examples/CRISP/client/src/hooks/voting/useVoteCasting.ts b/examples/CRISP/client/src/hooks/voting/useVoteCasting.ts index 7f183b74f..69792c5d0 100644 --- a/examples/CRISP/client/src/hooks/voting/useVoteCasting.ts +++ b/examples/CRISP/client/src/hooks/voting/useVoteCasting.ts @@ -178,7 +178,19 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo */ const handleVote = useCallback( async (pollSelected: Poll, slotAddress: string): Promise => { + // [CRISP-DIAG] See castVote() in DailyPoll.tsx for context. These logs + // identify whether handleVote reaches signMessageAsync at all, and + // whether signMessageAsync resolves or throws — Playwright observes + // them via the console listener wired in test/crisp.spec.ts. + console.error('[CRISP-DIAG] handleVote: entered', { + hasRoundState: Boolean(roundState), + roundId: roundState?.id, + pollValue: pollSelected.value, + slotAddress, + }) + if (!roundState) { + console.error('[CRISP-DIAG] handleVote: !roundState — throwing before setStepMessage') throw new Error('No round state available for voting') } @@ -186,6 +198,7 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo setVotingStep('signing') setLastActiveStep('signing') setStepMessage('Please sign the message in your wallet...') + console.error('[CRISP-DIAG] handleVote: step="signing" set, message set, about to await signMessageAsync') const message = `Vote for round ${roundState.id}` const messageHash = hashMessage(message) @@ -197,6 +210,7 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo let signature: string try { signature = await signMessageAsync({ message }) + console.error('[CRISP-DIAG] handleVote: signMessageAsync resolved, signature length =', signature?.length) return { signature, messageHash, @@ -205,7 +219,10 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo balance, } } catch (error) { - console.log('User rejected signature or signing failed', error) + console.error( + '[CRISP-DIAG] handleVote: signMessageAsync THREW — resetting votingState (this clears the "Please sign…" text before Playwright can observe it)', + error, + ) resetVotingState() return { signature: '', @@ -222,13 +239,25 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo const castVoteWithProof = useCallback( async (pollSelected: Poll | null, isAMask: boolean = false) => { + // [CRISP-DIAG] See castVote() in DailyPoll.tsx for context. + console.error('[CRISP-DIAG] castVoteWithProof: entered', { + isAMask, + hasPollSelected: Boolean(pollSelected), + hasUser: Boolean(user), + hasRoundState: Boolean(roundState), + roundId: roundState?.id, + }) + if (!isAMask && !pollSelected) { - console.log('Cannot cast vote: Poll option not selected.') + console.error('[CRISP-DIAG] castVoteWithProof: EARLY EXIT — !isAMask && !pollSelected') showToast({ type: 'danger', message: 'Please select a poll option first.' }) return } if (!user || !roundState) { - console.error('Cannot cast vote: Missing user or round state.') + console.error('[CRISP-DIAG] castVoteWithProof: EARLY EXIT — !user || !roundState', { + hasUser: Boolean(user), + hasRoundState: Boolean(roundState), + }) showToast({ type: 'danger', message: 'Cannot cast vote. Ensure you are connected, and the round is active.', diff --git a/examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx b/examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx index 619b498c4..261c534c3 100644 --- a/examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx +++ b/examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx @@ -134,11 +134,26 @@ const DailyPollSection: React.FC = ({ loading, endTime, t } const castVote = async (isMasking: boolean) => { + // [CRISP-DIAG] Temporary instrumentation: the e2e CI is racing here when + // wagmi hasn't populated `user` by the time the test clicks Cast (after a + // post-DKG page reload). The branch silently opens the ConnectKit modal + // and Playwright sees no signing prompt → 60s timeout. Tag every + // diagnostic with `[CRISP-DIAG]` so it's trivially greppable in CI logs + // and obvious which lines to delete once the real fix lands. if (!user) { + console.error('[CRISP-DIAG] castVote: !user — opening connect modal instead of signing', { + isMasking, + pollSelected, + }) setOpen(true) return } + console.error('[CRISP-DIAG] castVote: dispatching castVoteWithProof', { + isMasking, + pollSelected, + userAddress: user.address, + }) await castVoteWithProof(pollSelected, isMasking) } diff --git a/examples/CRISP/client/src/providers/Web3Provider.tsx b/examples/CRISP/client/src/providers/Web3Provider.tsx index af002e728..8b6842993 100644 --- a/examples/CRISP/client/src/providers/Web3Provider.tsx +++ b/examples/CRISP/client/src/providers/Web3Provider.tsx @@ -5,7 +5,6 @@ // or FITNESS FOR A PARTICULAR PURPOSE. import { WagmiProvider, createConfig, http } from 'wagmi' -import { sepolia, anvil } from 'wagmi/chains' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ConnectKitProvider, getDefaultConfig } from 'connectkit' import React from 'react' @@ -15,13 +14,15 @@ const walletConnectProjectId = import.meta.env.VITE_WALLETCONNECT_PROJECT_ID || if (!walletConnectProjectId) console.warn('VITE_WALLETCONNECT_PROJECT_ID is not set in .env file. WalletConnect will not function properly.') +const chain = getChain() +const rpcUrl = import.meta.env.VITE_RPC_URL || 'http://127.0.0.1:8545' + const config = createConfig( getDefaultConfig({ appName: 'CRISP', - chains: [getChain()], + chains: [chain], transports: { - [anvil.id]: http(anvil.rpcUrls.default.http[0]), - [sepolia.id]: http(sepolia.rpcUrls.default.http[0]), + [chain.id]: http(rpcUrl), }, walletConnectProjectId: walletConnectProjectId, }), diff --git a/examples/CRISP/client/src/utils/methods.ts b/examples/CRISP/client/src/utils/methods.ts index 12974640f..219e82bf0 100644 --- a/examples/CRISP/client/src/utils/methods.ts +++ b/examples/CRISP/client/src/utils/methods.ts @@ -23,6 +23,8 @@ export const convertTimestampToDate = (timestamp: number, secondsToAdd: number = } export const getChain = (): Chain => { + const chainId = import.meta.env.VITE_CHAIN_ID + if (chainId === '31337' || chainId === 31337) return anvil return import.meta.env.DEV ? anvil : sepolia } diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index 9d964af47..1fcc280d4 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -95,22 +95,59 @@ async function waitForDemoPollReady(page: Page) { await expect(page.locator('.tag.live')).toBeVisible({ timeout: 60_000 }) } -async function castVoteWithSignature(page: Page, metamask: MetaMask) { - log(`clicking first vote card...`) - await page.locator("[data-test-id='poll-button-0']").click() +async function waitForWalletSession(page: Page) { + log('waiting for wallet session...') + await expect(page.locator('button:has-text("Connect Wallet")')).toHaveCount(0, { timeout: 60_000 }) + // ConnectKit shows a truncated address once wagmi isConnected; VoteManagement only + // sets `user` from the same source, so this gates Cast → signMessage. + await expect(page.locator('button').filter({ hasText: /^0x/i })).toBeVisible({ timeout: 60_000 }) + // Vote status fetch proves user.address + currentRoundId are wired in React context. + await expect(page.locator('.tag').filter({ hasText: 'Checking' })).toHaveCount(0, { timeout: 90_000 }) + log('wallet session ready') +} - const castBtn = page.locator('button:has-text("Cast")') - await expect(castBtn).toBeEnabled({ timeout: 30_000 }) +async function reconnectWalletIfNeeded(page: Page, metamask: MetaMask) { + const connectWalletBtn = page.locator('button:has-text("Connect Wallet")') + if (await connectWalletBtn.isVisible({ timeout: 3_000 }).catch(() => false)) { + log('wallet disconnected — reconnecting...') + await connectWalletWithRetry(page) + await metamask.connectToDapp() + } +} - log(`clicking Cast Vote...`) - await castBtn.click() +async function castVoteWithSignature(page: Page, metamask: MetaMask, diagLog: string[]) { + for (let attempt = 1; attempt <= 3; attempt++) { + try { + log(`clicking first vote card (attempt ${attempt})...`) + await page.locator("[data-test-id='poll-button-0']").click() - // useVoteCasting sets StepMessage with trailing "..."; getByText defaults to exact match - log(`waiting for wallet signing prompt...`) - await expect(page.getByText('Please sign the message in your wallet', { exact: false })).toBeVisible({ timeout: 60_000 }) + const castBtn = page.locator('button:has-text("Cast")') + await expect(castBtn).toBeEnabled({ timeout: 30_000 }) - log(`confirming MetaMask signature request...`) - await metamask.confirmSignature() + log(`clicking Cast Vote (attempt ${attempt})...`) + await castBtn.click() + log(`confirming MetaMask signature request...`) + await metamask.confirmSignature() + return + } catch (error) { + // [CRISP-DIAG] On failure, dump any console.error lines tagged with + // `[CRISP-DIAG]` so the CI log shows exactly which app-side branch + // fired (e.g. `!user`, `!roundState`, `signMessageAsync threw`). + // Without this dump, the only signal is a 60s Playwright timeout. + const diag = diagLog.filter((line) => line.includes('[CRISP-DIAG]')) + if (diag.length > 0) { + log(`--- [CRISP-DIAG] captured browser console (${diag.length} lines) ---`) + for (const line of diag) log(` ${line}`) + log(`--- end [CRISP-DIAG] ---`) + } else { + log(`[CRISP-DIAG] no diagnostic console lines were captured before failure`) + } + if (attempt === 3) throw error + log(`signature attempt ${attempt} failed, retrying...`) + await page.keyboard.press('Escape').catch(() => {}) + await page.waitForTimeout(2_000) + } + } } async function connectWalletWithRetry(page: Page, maxAttempts = 3) { @@ -141,8 +178,16 @@ async function connectWalletWithRetry(page: Page, maxAttempts = 3) { } test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) => { + // [CRISP-DIAG] Capture every browser console message so we can replay the + // diagnostic-tagged ones if `castVoteWithSignature` fails. Without this + // buffer the failure mode is a 60s Playwright timeout with no app-side + // breadcrumbs. Remove together with the `[CRISP-DIAG]` console.error calls + // in `useVoteCasting.ts` / `DailyPoll.tsx` once the cast-vote race is fixed. + const diagLog: string[] = [] page.on('console', (msg: ConsoleMessage) => { - console.log(msg.text()) + const text = msg.text() + console.log(text) + diagLog.push(text) }) log('============================================') @@ -175,11 +220,18 @@ test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) => const DKG_DURATION = Date.now() - testStart log(`DKG duration: ${DKG_DURATION}ms`) log(`forcing page reload...`) + const voteStatusReady = page.waitForResponse((resp) => resp.url().includes('/voting/status') && resp.ok(), { timeout: 120_000 }) await page.reload() + await page.waitForLoadState('load') log(`ensuring local anvil network after reload...`) await metamask.switchNetwork('localwallet') + await reconnectWalletIfNeeded(page, metamask) await waitForDemoPollReady(page) - await castVoteWithSignature(page, metamask) + await waitForWalletSession(page) + await voteStatusReady.catch(() => { + log('vote status response not observed (may have completed before listener)') + }) + await castVoteWithSignature(page, metamask, diagLog) const WAIT = E3_DURATION - DKG_DURATION + OUTPUT_DECRYPTION_WAIT log(`waiting ${WAIT}ms...`) await page.waitForTimeout(WAIT) From 62ee4eba512f4547d7811fa9b7ca077f505cdab0 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sun, 24 May 2026 10:54:28 +0200 Subject: [PATCH 49/54] chore(crisp): remove [CRISP-DIAG] e2e instrumentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Diagnostic console.error calls + test-side log buffer added in 142034aeb to identify the post-reload cast-vote race. CI confirmed the race was in waitForWalletSession (other agent's fix) — actor-signed signatures recover cleanly, end-to-end is green. Restore the three touched files to their pre-instrumentation state. --- .../client/src/hooks/voting/useVoteCasting.ts | 35 ++----------------- .../pages/Landing/components/DailyPoll.tsx | 15 -------- examples/CRISP/test/crisp.spec.ts | 26 ++------------ 3 files changed, 6 insertions(+), 70 deletions(-) diff --git a/examples/CRISP/client/src/hooks/voting/useVoteCasting.ts b/examples/CRISP/client/src/hooks/voting/useVoteCasting.ts index 69792c5d0..7f183b74f 100644 --- a/examples/CRISP/client/src/hooks/voting/useVoteCasting.ts +++ b/examples/CRISP/client/src/hooks/voting/useVoteCasting.ts @@ -178,19 +178,7 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo */ const handleVote = useCallback( async (pollSelected: Poll, slotAddress: string): Promise => { - // [CRISP-DIAG] See castVote() in DailyPoll.tsx for context. These logs - // identify whether handleVote reaches signMessageAsync at all, and - // whether signMessageAsync resolves or throws — Playwright observes - // them via the console listener wired in test/crisp.spec.ts. - console.error('[CRISP-DIAG] handleVote: entered', { - hasRoundState: Boolean(roundState), - roundId: roundState?.id, - pollValue: pollSelected.value, - slotAddress, - }) - if (!roundState) { - console.error('[CRISP-DIAG] handleVote: !roundState — throwing before setStepMessage') throw new Error('No round state available for voting') } @@ -198,7 +186,6 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo setVotingStep('signing') setLastActiveStep('signing') setStepMessage('Please sign the message in your wallet...') - console.error('[CRISP-DIAG] handleVote: step="signing" set, message set, about to await signMessageAsync') const message = `Vote for round ${roundState.id}` const messageHash = hashMessage(message) @@ -210,7 +197,6 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo let signature: string try { signature = await signMessageAsync({ message }) - console.error('[CRISP-DIAG] handleVote: signMessageAsync resolved, signature length =', signature?.length) return { signature, messageHash, @@ -219,10 +205,7 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo balance, } } catch (error) { - console.error( - '[CRISP-DIAG] handleVote: signMessageAsync THREW — resetting votingState (this clears the "Please sign…" text before Playwright can observe it)', - error, - ) + console.log('User rejected signature or signing failed', error) resetVotingState() return { signature: '', @@ -239,25 +222,13 @@ export const useVoteCasting = (customRoundState?: VoteStateLite | null, customVo const castVoteWithProof = useCallback( async (pollSelected: Poll | null, isAMask: boolean = false) => { - // [CRISP-DIAG] See castVote() in DailyPoll.tsx for context. - console.error('[CRISP-DIAG] castVoteWithProof: entered', { - isAMask, - hasPollSelected: Boolean(pollSelected), - hasUser: Boolean(user), - hasRoundState: Boolean(roundState), - roundId: roundState?.id, - }) - if (!isAMask && !pollSelected) { - console.error('[CRISP-DIAG] castVoteWithProof: EARLY EXIT — !isAMask && !pollSelected') + console.log('Cannot cast vote: Poll option not selected.') showToast({ type: 'danger', message: 'Please select a poll option first.' }) return } if (!user || !roundState) { - console.error('[CRISP-DIAG] castVoteWithProof: EARLY EXIT — !user || !roundState', { - hasUser: Boolean(user), - hasRoundState: Boolean(roundState), - }) + console.error('Cannot cast vote: Missing user or round state.') showToast({ type: 'danger', message: 'Cannot cast vote. Ensure you are connected, and the round is active.', diff --git a/examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx b/examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx index 261c534c3..619b498c4 100644 --- a/examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx +++ b/examples/CRISP/client/src/pages/Landing/components/DailyPoll.tsx @@ -134,26 +134,11 @@ const DailyPollSection: React.FC = ({ loading, endTime, t } const castVote = async (isMasking: boolean) => { - // [CRISP-DIAG] Temporary instrumentation: the e2e CI is racing here when - // wagmi hasn't populated `user` by the time the test clicks Cast (after a - // post-DKG page reload). The branch silently opens the ConnectKit modal - // and Playwright sees no signing prompt → 60s timeout. Tag every - // diagnostic with `[CRISP-DIAG]` so it's trivially greppable in CI logs - // and obvious which lines to delete once the real fix lands. if (!user) { - console.error('[CRISP-DIAG] castVote: !user — opening connect modal instead of signing', { - isMasking, - pollSelected, - }) setOpen(true) return } - console.error('[CRISP-DIAG] castVote: dispatching castVoteWithProof', { - isMasking, - pollSelected, - userAddress: user.address, - }) await castVoteWithProof(pollSelected, isMasking) } diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts index 1fcc280d4..713a2ece3 100644 --- a/examples/CRISP/test/crisp.spec.ts +++ b/examples/CRISP/test/crisp.spec.ts @@ -115,7 +115,7 @@ async function reconnectWalletIfNeeded(page: Page, metamask: MetaMask) { } } -async function castVoteWithSignature(page: Page, metamask: MetaMask, diagLog: string[]) { +async function castVoteWithSignature(page: Page, metamask: MetaMask) { for (let attempt = 1; attempt <= 3; attempt++) { try { log(`clicking first vote card (attempt ${attempt})...`) @@ -130,18 +130,6 @@ async function castVoteWithSignature(page: Page, metamask: MetaMask, diagLog: st await metamask.confirmSignature() return } catch (error) { - // [CRISP-DIAG] On failure, dump any console.error lines tagged with - // `[CRISP-DIAG]` so the CI log shows exactly which app-side branch - // fired (e.g. `!user`, `!roundState`, `signMessageAsync threw`). - // Without this dump, the only signal is a 60s Playwright timeout. - const diag = diagLog.filter((line) => line.includes('[CRISP-DIAG]')) - if (diag.length > 0) { - log(`--- [CRISP-DIAG] captured browser console (${diag.length} lines) ---`) - for (const line of diag) log(` ${line}`) - log(`--- end [CRISP-DIAG] ---`) - } else { - log(`[CRISP-DIAG] no diagnostic console lines were captured before failure`) - } if (attempt === 3) throw error log(`signature attempt ${attempt} failed, retrying...`) await page.keyboard.press('Escape').catch(() => {}) @@ -178,16 +166,8 @@ async function connectWalletWithRetry(page: Page, maxAttempts = 3) { } test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) => { - // [CRISP-DIAG] Capture every browser console message so we can replay the - // diagnostic-tagged ones if `castVoteWithSignature` fails. Without this - // buffer the failure mode is a 60s Playwright timeout with no app-side - // breadcrumbs. Remove together with the `[CRISP-DIAG]` console.error calls - // in `useVoteCasting.ts` / `DailyPoll.tsx` once the cast-vote race is fixed. - const diagLog: string[] = [] page.on('console', (msg: ConsoleMessage) => { - const text = msg.text() - console.log(text) - diagLog.push(text) + console.log(msg.text()) }) log('============================================') @@ -231,7 +211,7 @@ test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) => await voteStatusReady.catch(() => { log('vote status response not observed (may have completed before listener)') }) - await castVoteWithSignature(page, metamask, diagLog) + await castVoteWithSignature(page, metamask) const WAIT = E3_DURATION - DKG_DURATION + OUTPUT_DECRYPTION_WAIT log(`waiting ${WAIT}ms...`) await page.waitForTimeout(WAIT) From defde70e037a470b7408dcbe8e0dff8164419e98 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sun, 24 May 2026 10:57:27 +0200 Subject: [PATCH 50/54] update docs --- docs/pages/cryptography.mdx | 34 +++++++----- docs/pages/internals/dkg.mdx | 100 +++++++++++++++++++++++++++-------- 2 files changed, 101 insertions(+), 33 deletions(-) diff --git a/docs/pages/cryptography.mdx b/docs/pages/cryptography.mdx index 2d4c6bd3c..abc0baf0f 100644 --- a/docs/pages/cryptography.mdx +++ b/docs/pages/cryptography.mdx @@ -265,21 +265,31 @@ When a node misbehaves, the off-chain accusation quorum protocol produces a call. The on-chain check enforces, in `_verifyAttestationEvidence`: 1. **Voter quorum and signatures**: ≥ `M` committee-member voters (sorted ascending, accused - excluded), each signing `(VOTE_TYPEHASH, chainId, e3Id, accusationId, voter, agrees, dataHash)` - over the same accusation. + excluded), each signing the canonical EIP-712 typed-data hash over + `AccusationVote(uint256 e3Id, bytes32 accusationId, address voter, bytes32 dataHash, uint256 deadline)`. + `chainId` and `verifyingContract = address(SlashingManager)` live in the EIP-712 domain, not the + struct — so signatures can't be replayed across chains or against a different contract. 2. **Same evidence across voters**: all `dataHashes[i]` must equal a common `dataHash`. Without this each voter could be agreeing to "something bad" of their own — the quorum would not actually agree on what they observed. -3. **Evidence preimage on chain**: the proposal payload includes a `bytes evidence` field, and - `keccak256(evidence) == commonDataHash` is required. The evidence is the raw - `abi.encode(proof.data, public_signals)` of the faulty proof, so the contract — and any third - party reading the transaction calldata — can audit exactly which bytes the committee voted on, - not just a hash. - -Combined with the fold-attestation chain, the slashing flow forms a complete attribution chain: -operator → registered address at `topNodes[partyId]` → signature on a specific commitment → faulty -proof bytes auditable on chain. Both the DKG fault-attribution side and the slashing-evidence side -are cryptographically bound on chain. +3. **Shared signature deadline**: every voter signs the same `deadline` (chosen by the accuser), and + the contract enforces `block.timestamp <= deadline`. Stolen signatures can't be replayed + indefinitely; the validity window is governance-mutable via the registry's + `accusationVoteValidity`. +4. **Agreement is implicit**: the gossip wire no longer carries disagreement — a peer who finds the + disputed proof passes simply stays silent. Every signature submitted in evidence is an agreement, + so an `agrees` flag is no longer signed or transported. + +The faulty-proof bytes themselves are kept as off-chain audit metadata on the +`AccusationQuorumReached` event so any observer can independently re-derive each voter's `dataHash`; +they are intentionally not on chain (the `dataHash` equality check is all the contract needs for +attribution, and dropping the preimage saves significant calldata). + +Combined with the fold-attestation chain, the slashing flow still forms a complete attribution +chain: operator → registered address at `topNodes[partyId]` → EIP-712 signature on a specific +`(e3Id, accusationId, dataHash, deadline)` tuple → off-chain audit bytes recovering the original +proof. Both the DKG fault-attribution side and the slashing-evidence side are cryptographically +bound on chain. #### Committee hash binding diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index bf7be16e7..33408a0eb 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -410,22 +410,31 @@ read to identify which registered operator produced which DKG output. The mappin (via committee hash), the attestation binds to the proof commitments and the operator key, and the operator key was registered against `topNodes` at committee finalisation. -### Fold-attestation verifier rotation (operator restart requirement) +### Registry-backed runtime config (operator restart requirement) + +Two registry storage values are fetched **once at process startup** and reused for the entire +process lifetime: + +1. `dkgFoldAttestationVerifier` — the EIP-712 `verifyingContract` for fold attestations (see + [`fetch_dkg_fold_attestation_verifier`](https://github.com/gnosisguild/enclave/blob/main/crates/evm/src/ciphernode_registry_sol.rs)). +2. `accusationVoteValidity` — the seconds-window applied to accusation vote `deadline`s (see the + [Registry-wide vote validity window](#registry-wide-vote-validity-window) subsection). + +Both follow the same restart-required lifecycle: governance updates take effect for new processes +only, and live nodes keep using the value they fetched at startup. + +#### Verifier rotation The address of the on-chain `DkgFoldAttestationVerifier` is governance-mutable on the registry via a 2-day timelocked propose / commit / cancel flow (`DKG_FOLD_VERIFIER_TIMELOCK`). This is intentional — the verifier is treated as a critical admin key so a compromised owner cannot instantly swap in a weakened verifier that bypasses per-party attestation checks. -**Node-operator requirement.** Ciphernodes fetch `dkgFoldAttestationVerifier()` from the registry -**once at process startup** and use the returned address as the EIP-712 `verifyingContract` for -every fold attestation they sign during the process lifetime (see -[`fetch_dkg_fold_attestation_verifier`](https://github.com/gnosisguild/enclave/blob/main/crates/evm/src/ciphernode_registry_sol.rs) -and the `setup_zk_actors` call in `ciphernode-builder`). After a successful -`commitDkgFoldAttestationVerifier`, signatures produced by long-running nodes will be rejected -on-chain by the new verifier — different `verifyingContract` means a different EIP-712 domain -separator, so `ECDSA.recover` returns the wrong address, the canonical-slot binding check fails, and -aggregators will treat those nodes as dishonest until they restart. +**Node-operator requirement.** After a successful `commitDkgFoldAttestationVerifier`, signatures +produced by long-running nodes will be rejected on-chain by the new verifier — different +`verifyingContract` means a different EIP-712 domain separator, so `ECDSA.recover` returns the wrong +address, the canonical-slot binding check fails, and aggregators will treat those nodes as dishonest +until they restart. The 2-day timelock window exists for exactly this reason: it gives operators time to coordinate a rolling restart before the swap becomes effective. Operators MUST restart all ciphernodes within @@ -434,6 +443,16 @@ recommendation. Nodes that miss the window silently produce invalid attestations The same rule applies to the one-shot deploy-time `setInitialDkgFoldAttestationVerifier`, except that call is only valid when no verifier is yet configured. +#### Vote validity changes + +`setAccusationVoteValidity` is not timelocked — governance can change the window instantly. The +restart requirement is therefore weaker than for the verifier (an updated window doesn't reject old +signatures, it only shortens or extends the freshness check for future ones), but the same "live +nodes keep stamping deadlines from the old value" caveat applies. After a non-trivial shortening, +coordinate a rolling restart so every node observes the new window before any votes are signed +against it. See the [Registry-wide vote validity window](#registry-wide-vote-validity-window) +subsection for the emergency-stop semantics (setting it to 0). + **Multi-chain caveat.** When `enclave.config.yaml` lists more than one chain, the node reads `dkgFoldAttestationVerifier` and `accusationVoteValidity` from the **first enabled** chain only (`fetch_dkg_fold_attestation_verifier_from_registry` / @@ -502,22 +521,61 @@ differs the proof is rejected — and the plaintext is decoded from the proof's When fault attribution leads to a slash, the off-chain accusation quorum produces a payload that [`SlashingManager.proposeSlash`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/slashing/SlashingManager.sol) -decodes as `(proofType, voters[], agrees[], dataHashes[], signatures[], evidence)`. +decodes as +`(uint256 proofType, address[] voters, bytes32[] dataHashes, uint256 deadline, bytes[] signatures)`. `_verifyAttestationEvidence` enforces: -- ≥ `M` voters, sorted ascending, accused excluded, each signing the standard - `VOTE_TYPEHASH(chainId, e3Id, accusationId, voter, agrees, dataHash)` digest. +- ≥ `M` voters, sorted ascending, accused excluded, each producing a canonical EIP-712 typed-data + signature over + `AccusationVote(uint256 e3Id, bytes32 accusationId, address voter, bytes32 dataHash, uint256 deadline)`. + `chainId` and `verifyingContract = address(SlashingManager)` live in the EIP-712 domain — see + `EIP712_DOMAIN_NAME` ("EnclaveSlashing") and `EIP712_DOMAIN_VERSION` ("1") on the contract; the + Rust signer reads the exact same strings from `e3_events::VOTE_DOMAIN_NAME` / + `VOTE_DOMAIN_VERSION` / `VOTE_TYPEHASH_STR`. - All `dataHashes[i]` equal a common `dataHash` (voters must agree on the same evidence, not just "something bad"). -- `keccak256(evidence) == commonDataHash`. The `evidence` field is the raw - `abi.encode(proof.data, public_signals)` of the faulty proof, transported on-chain so the contract - — and any third party — can recompute the dataHash from the bytes the committee voted on. - -On the Rust side, the evidence bytes are populated for every accusation path that leads to a slash -(ZK verification failures via `ProofVerificationFailed`, forwarded C3a/C3b accusations via the +- `block.timestamp <= deadline`. Every voter signs the same `deadline` value — the accuser stamps it + at proposal time as `now + accusationVoteValidity` (a registry-wide governance knob, see the + subsection below), and `AccusationManager::on_vote_received` rejects peers' votes whose deadline + disagrees so the aggregated evidence carries one shared value. Stolen signatures can't be replayed + indefinitely. +- Agreement is implicit: there is no `agrees` field on the signed struct or on the gossip wire. A + peer who finds the disputed proof passes simply stays silent; every signature reaching the + on-chain decoder is an affirmative attestation. See the + [`AccusationVote` docstring](https://github.com/gnosisguild/enclave/blob/main/crates/events/src/enclave_event/accusation_vote.rs) + for the rationale and the fast-fail trade-off this design accepts. + +The faulty-proof preimage (`abi.encode(proof.data, public_signals)`) is **not** transported on chain +— the previous `keccak256(evidence) == commonDataHash` check was dropped because the `dataHash` +equality across voters is sufficient for attribution and the calldata cost of carrying ~16 KB of +preimage per slash is not. The preimage is kept on the off-chain `AccusationQuorumReached` event as +audit metadata for observers; populated for every accusation path that leads to a slash (ZK +verification failures via `ProofVerificationFailed`, forwarded C3a/C3b accusations via the re-verification path, and commitment-consistency violations via `CommitmentConsistencyViolation`). -This means every slash carries auditable evidence to chain — there is no path that produces a slash -without binding voter signatures to specific proof bytes. + +#### Registry-wide vote validity window + +The `accusationVoteValidity` storage on +[`CiphernodeRegistryOwnable`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol) +(default 30 minutes, seeded by `DEFAULT_ACCUSATION_VOTE_VALIDITY` at `initialize`) is the +seconds-window applied to every accusation vote's `deadline`. Ciphernodes fetch it once at process +startup via `fetch_accusation_vote_validity_from_registry` and reuse the value for the process +lifetime — same lifecycle as `dkgFoldAttestationVerifier`. + +Governance can change it with `setAccusationVoteValidity(uint256)`. Two notable knob positions: + +- **Shorter window**: tighter freshness — useful if stolen-signature replay becomes a concern, at + the cost of giving on-chain submitters less slack to land the `proposeSlash` transaction after + off-chain quorum reaches `M`. +- **Zero**: effectively disables slashing submission. Every new vote's `deadline` collapses to + "now", which the on-chain check rejects immediately. This is the emergency stop — useful while + investigating a runaway accusation but should be restored quickly to keep the slashing path + operational. + +In-flight ciphernodes do not pick up the new value until restart. After a non-trivial shortening +(e.g. 30 min → 1 min) operators should coordinate a rolling restart, otherwise live nodes will keep +stamping deadlines from the old window and produce votes that the on-chain check rejects when the +round closes. --- From 306e64797724be94dcf1d89090a53c52c7235a2d Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sun, 24 May 2026 11:32:47 +0200 Subject: [PATCH 51/54] keep EIP-712 registry per chain instead of global --- .../src/ciphernode_builder.rs | 93 +++++++++++-------- .../src/actors/accusation_manager_ext.rs | 34 +++++-- crates/zk-prover/src/actors/mod.rs | 13 +-- .../src/actors/node_proof_aggregator.rs | 37 ++++++-- 4 files changed, 115 insertions(+), 62 deletions(-) diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 617e36ba1..7b0960a81 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -241,25 +241,24 @@ impl CiphernodeBuilder { }) } - /// Fetch `CiphernodeRegistry.dkgFoldAttestationVerifier()` at startup (EIP-712 verifying contract). + /// Fetch `CiphernodeRegistry.dkgFoldAttestationVerifier()` for one chain (EIP-712 verifying contract). async fn fetch_dkg_fold_attestation_verifier_from_registry( provider_cache: &mut ProviderCache, - chains: &[ChainConfig], + chain: &ChainConfig, ) -> Result> { - let Some(chain) = chains.iter().find(|c| c.enabled.unwrap_or(true)) else { - return Ok(None); - }; let provider = provider_cache.ensure_read_provider(chain).await?; let registry = chain.contracts.ciphernode_registry.address()?; let verifier = fetch_dkg_fold_attestation_verifier(provider.provider(), registry).await?; if verifier.is_none() { tracing::warn!( + chain = %chain.name, registry = %registry, "CiphernodeRegistry.dkgFoldAttestationVerifier is not set on-chain; \ nodes will not sign DKG fold attestations when proof aggregation is enabled" ); } else if let Some(addr) = verifier { info!( + chain = %chain.name, registry = %registry, verifier = %addr, "loaded dkgFoldAttestationVerifier from CiphernodeRegistry" @@ -268,11 +267,10 @@ impl CiphernodeBuilder { Ok(verifier) } - /// Fetch `CiphernodeRegistry.accusationVoteValidity()` at startup (off-chain + /// Fetch `CiphernodeRegistry.accusationVoteValidity()` for one chain (off-chain /// vote freshness window in seconds). Returns `0` when the registry has - /// disabled slashing (governance emergency stop) or when no chain is - /// configured (in-process benchmarks); the actor will then refuse to stamp - /// votes that would be rejected on chain. + /// disabled slashing (governance emergency stop). The actor will then refuse + /// to stamp votes that would be rejected on chain. /// /// The `u256` returned by the registry is clamped to `u64`. The contract /// has no upper bound but `u64::MAX` seconds is already ~5.8 × 10¹¹ years — @@ -281,11 +279,8 @@ impl CiphernodeBuilder { /// comparison. async fn fetch_accusation_vote_validity_from_registry( provider_cache: &mut ProviderCache, - chains: &[ChainConfig], + chain: &ChainConfig, ) -> Result { - let Some(chain) = chains.iter().find(|c| c.enabled.unwrap_or(true)) else { - return Ok(0); - }; let provider = provider_cache.ensure_read_provider(chain).await?; let registry = chain.contracts.ciphernode_registry.address()?; let validity = fetch_accusation_vote_validity(provider.provider(), registry).await?; @@ -293,6 +288,7 @@ impl CiphernodeBuilder { Some(v) => { let clamped: u64 = v.try_into().unwrap_or(u64::MAX); info!( + chain = %chain.name, registry = %registry, accusation_vote_validity_secs = clamped, "loaded accusationVoteValidity from CiphernodeRegistry" @@ -301,6 +297,7 @@ impl CiphernodeBuilder { } None => { tracing::warn!( + chain = %chain.name, registry = %registry, "CiphernodeRegistry.accusationVoteValidity is 0; the off-chain \ accusation manager will not produce votes (governance-disabled \ @@ -585,37 +582,52 @@ impl CiphernodeBuilder { let needs_zk_actors = self.keyshare.is_some() || (self.pubkey_agg && self.keyshare.is_none()); - let dkg_fold_verifier_addr = if needs_zk_actors { + let mut dkg_fold_verifier_by_chain: HashMap> = HashMap::new(); + if needs_zk_actors { if !self.chains.is_empty() { - Self::fetch_dkg_fold_attestation_verifier_from_registry( - &mut provider_cache, - &self.chains, - ) - .await? + for chain in self.chains.iter().filter(|c| c.enabled.unwrap_or(true)) { + let provider = provider_cache.ensure_read_provider(chain).await?; + let chain_id = provider.chain_id(); + validate_chain_id(chain, chain_id)?; + let verifier = Self::fetch_dkg_fold_attestation_verifier_from_registry( + &mut provider_cache, + chain, + ) + .await?; + dkg_fold_verifier_by_chain.insert(chain_id, verifier); + } } else { // In-process benchmark harness (no EVM chains): optional env override. - std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER") + if let Some(verifier) = std::env::var("BENCHMARK_DKG_FOLD_ATTESTATION_VERIFIER") .ok() .and_then(|s| s.parse().ok()) + { + dkg_fold_verifier_by_chain.insert(benchmark_default_chain_id(), Some(verifier)); + } } - } else { - None - }; + } - // Off-chain freshness window for accusation votes — fetched alongside - // the verifier so AccusationManagerExtension (created below) has - // everything it needs without re-walking the chain. Benchmark harness - // falls back to the env var so deterministic builds don't require - // RPC access. - let accusation_vote_validity_secs = if !self.chains.is_empty() { - Self::fetch_accusation_vote_validity_from_registry(&mut provider_cache, &self.chains) - .await? + // Off-chain freshness window for accusation votes — fetched per chain so + // AccusationManagerExtension can look up by `e3_id.chain_id()`. Benchmark + // harness falls back to the env var so deterministic builds don't require RPC. + let mut accusation_vote_validity_by_chain: HashMap = HashMap::new(); + if !self.chains.is_empty() { + for chain in self.chains.iter().filter(|c| c.enabled.unwrap_or(true)) { + let provider = provider_cache.ensure_read_provider(chain).await?; + let chain_id = provider.chain_id(); + validate_chain_id(chain, chain_id)?; + let validity = + Self::fetch_accusation_vote_validity_from_registry(&mut provider_cache, chain) + .await?; + accusation_vote_validity_by_chain.insert(chain_id, validity); + } } else { - std::env::var("BENCHMARK_ACCUSATION_VOTE_VALIDITY_SECS") + let validity = std::env::var("BENCHMARK_ACCUSATION_VOTE_VALIDITY_SECS") .ok() .and_then(|s| s.parse::().ok()) - .unwrap_or(0) - }; + .unwrap_or(0); + accusation_vote_validity_by_chain.insert(benchmark_default_chain_id(), validity); + } // E3 specific setup let mut e3_builder = E3Router::builder(&bus, store.clone()); @@ -640,7 +652,7 @@ impl CiphernodeBuilder { )); info!("Setting up ZK actors"); - setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_addr); + setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_by_chain.clone()); } if self.pubkey_agg { @@ -661,7 +673,7 @@ impl CiphernodeBuilder { .ok_or_else(|| anyhow::anyhow!("ZK backend is required for aggregator"))?; let signer = provider_cache.ensure_signer().await?; info!("Setting up ZK actors for aggregator"); - setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_addr); + setup_zk_actors(&bus, backend, signer, dkg_fold_verifier_by_chain.clone()); } } @@ -678,14 +690,14 @@ impl CiphernodeBuilder { let signer = provider_cache.ensure_signer().await?; let slashing_manager_addr = self.resolve_slashing_manager()?; info!( - vote_validity_secs = accusation_vote_validity_secs, + chains = accusation_vote_validity_by_chain.len(), "Setting up AccusationManagerExtension" ); e3_builder = e3_builder.with(AccusationManagerExtension::create( &bus, signer, slashing_manager_addr, - accusation_vote_validity_secs, + accusation_vote_validity_by_chain, )); } @@ -785,6 +797,11 @@ impl CiphernodeBuilder { } } +/// Chain id used by in-process benchmark harnesses when no EVM chains are configured. +fn benchmark_default_chain_id() -> u64 { + 1 +} + /// Validate chain ID matches expected configuration fn validate_chain_id(chain: &ChainConfig, actual_chain_id: u64) -> Result<()> { if let Some(expected_chain_id) = chain.chain_id { diff --git a/crates/zk-prover/src/actors/accusation_manager_ext.rs b/crates/zk-prover/src/actors/accusation_manager_ext.rs index a41691c0c..127eef442 100644 --- a/crates/zk-prover/src/actors/accusation_manager_ext.rs +++ b/crates/zk-prover/src/actors/accusation_manager_ext.rs @@ -10,6 +10,8 @@ //! Listens for [`CommitteeFinalized`], reads `threshold_m` from [`E3Meta`], //! parses committee addresses, and starts the actor with full context. +use std::collections::HashMap; + use crate::AccusationManager; use alloy::primitives::Address; use alloy::signers::local::PrivateKeySigner; @@ -17,18 +19,19 @@ use anyhow::Result; use async_trait::async_trait; use e3_events::{BusHandle, CommitteeFinalized, EnclaveEvent, EnclaveEventData, Event}; use e3_request::{E3Context, E3ContextSnapshot, E3Extension, META_KEY}; -use tracing::{error, info}; +use tracing::{error, info, warn}; pub struct AccusationManagerExtension { bus: BusHandle, signer: PrivateKeySigner, /// On-chain `SlashingManager` address (EIP-712 `verifyingContract` for vote sigs). slashing_manager: Address, - /// Registry-wide off-chain freshness window (seconds), read from + /// Per-chain off-chain freshness window (seconds), read from /// `CiphernodeRegistry.accusationVoteValidity()` at process startup. - /// Passed to every per-E3 actor; governance changes require a node restart - /// to take effect (same lifecycle contract as `slashing_manager`). - vote_validity_secs: u64, + /// Looked up by `e3_id.chain_id()` when each per-E3 actor starts; + /// governance changes require a node restart to take effect (same lifecycle + /// contract as `slashing_manager`). + vote_validity_secs_by_chain: HashMap, } impl AccusationManagerExtension { @@ -36,15 +39,28 @@ impl AccusationManagerExtension { bus: &BusHandle, signer: PrivateKeySigner, slashing_manager: Address, - vote_validity_secs: u64, + vote_validity_secs_by_chain: HashMap, ) -> Box { Box::new(Self { bus: bus.clone(), signer: signer.clone(), slashing_manager, - vote_validity_secs, + vote_validity_secs_by_chain, }) } + + fn vote_validity_secs_for(&self, chain_id: u64) -> u64 { + match self.vote_validity_secs_by_chain.get(&chain_id) { + Some(&secs) => secs, + None => { + warn!( + chain_id, + "no accusationVoteValidity configured for chain; accusation votes will not be stamped" + ); + 0 + } + } + } } #[async_trait] @@ -97,6 +113,8 @@ impl E3Extension for AccusationManagerExtension { threshold_m ); + let vote_validity_secs = self.vote_validity_secs_for(e3_id.chain_id()); + let addr = AccusationManager::setup( &self.bus, e3_id, @@ -104,7 +122,7 @@ impl E3Extension for AccusationManagerExtension { self.slashing_manager, committee_addresses, threshold_m, - self.vote_validity_secs, + vote_validity_secs, meta.params_preset, ); diff --git a/crates/zk-prover/src/actors/mod.rs b/crates/zk-prover/src/actors/mod.rs index d3525f69f..92c2d3953 100644 --- a/crates/zk-prover/src/actors/mod.rs +++ b/crates/zk-prover/src/actors/mod.rs @@ -30,7 +30,7 @@ //! let signer = PrivateKeySigner::random(); //! //! // Setup all actors with proper separation of concerns -//! setup_zk_actors(&bus, &backend, signer, None); +//! setup_zk_actors(&bus, &backend, signer, HashMap::new()); //! ``` pub mod accusation_manager; @@ -59,6 +59,7 @@ use actix::{Actor, Addr}; use alloy::primitives::Address; use alloy::signers::local::PrivateKeySigner; use e3_events::BusHandle; +use std::collections::HashMap; use crate::ZkBackend; @@ -66,14 +67,14 @@ use crate::ZkBackend; /// /// Requires a `ZkBackend` for proof generation/verification and a /// `PrivateKeySigner` for signing proofs (fault attribution). -/// `dkg_fold_attestation_verifier` is `CiphernodeRegistry.dkgFoldAttestationVerifier()` -/// (EIP-712 `verifyingContract` for fold attestations). Fetched at node startup when -/// proof aggregation is enabled. +/// `dkg_fold_attestation_verifiers_by_chain` maps each enabled chain's id to +/// `CiphernodeRegistry.dkgFoldAttestationVerifier()` (EIP-712 `verifyingContract` +/// for fold attestations). Fetched at node startup when proof aggregation is enabled. pub fn setup_zk_actors( bus: &BusHandle, backend: &ZkBackend, signer: PrivateKeySigner, - dkg_fold_attestation_verifier: Option
, + dkg_fold_attestation_verifiers_by_chain: HashMap>, ) -> ZkActors { let zk_actor = ZkActor::new(backend).start(); let verifier = zk_actor.clone().recipient(); @@ -82,7 +83,7 @@ pub fn setup_zk_actors( let proof_verification = ProofVerificationActor::setup(bus, verifier); let share_verification = ShareVerificationActor::setup(bus); let node_proof_aggregator = - NodeProofAggregator::setup(bus, signer, dkg_fold_attestation_verifier); + NodeProofAggregator::setup(bus, signer, dkg_fold_attestation_verifiers_by_chain); ZkActors { zk_actor, diff --git a/crates/zk-prover/src/actors/node_proof_aggregator.rs b/crates/zk-prover/src/actors/node_proof_aggregator.rs index a06100128..6fa6e0c6f 100644 --- a/crates/zk-prover/src/actors/node_proof_aggregator.rs +++ b/crates/zk-prover/src/actors/node_proof_aggregator.rs @@ -49,9 +49,9 @@ struct DkgProofCollectionState { pub struct NodeProofAggregator { bus: BusHandle, signer: PrivateKeySigner, - /// On-chain `DkgFoldAttestationVerifier` address (EIP-712 `verifyingContract`). - /// `None` is only valid when proof aggregation will never run for this node. - dkg_fold_attestation_verifier: Option
, + /// Per-chain `DkgFoldAttestationVerifier` address (EIP-712 `verifyingContract`). + /// Looked up by `e3_id.chain_id()` when signing fold attestations. + dkg_fold_attestation_verifiers_by_chain: HashMap>, states: HashMap, fold_correlation: HashMap, pending_inner_proofs: HashMap>, @@ -61,12 +61,12 @@ impl NodeProofAggregator { pub fn new( bus: &BusHandle, signer: PrivateKeySigner, - dkg_fold_attestation_verifier: Option
, + dkg_fold_attestation_verifiers_by_chain: HashMap>, ) -> Self { Self { bus: bus.clone(), signer, - dkg_fold_attestation_verifier, + dkg_fold_attestation_verifiers_by_chain, states: HashMap::new(), fold_correlation: HashMap::new(), pending_inner_proofs: HashMap::new(), @@ -76,9 +76,9 @@ impl NodeProofAggregator { pub fn setup( bus: &BusHandle, signer: PrivateKeySigner, - dkg_fold_attestation_verifier: Option
, + dkg_fold_attestation_verifiers_by_chain: HashMap>, ) -> Addr { - let addr = Self::new(bus, signer, dkg_fold_attestation_verifier).start(); + let addr = Self::new(bus, signer, dkg_fold_attestation_verifiers_by_chain).start(); bus.subscribe(EventType::ThresholdSharePending, addr.clone().into()); bus.subscribe(EventType::DKGInnerProofReady, addr.clone().into()); bus.subscribe(EventType::ComputeResponse, addr.clone().into()); @@ -343,7 +343,9 @@ impl NodeProofAggregator { "NodeFold public party_id does not match sortition party_id" ); None - } else if let Some(verifying_contract) = self.dkg_fold_attestation_verifier { + } else if let Some(verifying_contract) = + self.dkg_fold_attestation_verifier_for(&e3_id) + { let payload = DkgFoldAttestationPayload { e3_id: e3_id.clone(), verifying_contract, @@ -503,6 +505,21 @@ impl Handler> for NodeProofAggregator { } impl NodeProofAggregator { + fn dkg_fold_attestation_verifier_for(&self, e3_id: &E3id) -> Option
{ + let chain_id = e3_id.chain_id(); + match self.dkg_fold_attestation_verifiers_by_chain.get(&chain_id) { + Some(Some(addr)) => Some(*addr), + Some(None) => None, + None => { + warn!( + chain_id, + "no dkgFoldAttestationVerifier configured for chain" + ); + None + } + } + } + fn handle_compute_response(&mut self, msg: TypedEvent) { let (msg, _ec) = msg.into_components(); if let ComputeResponseKind::Zk(ZkResponse::NodeDkgFold(resp)) = msg.response { @@ -581,7 +598,7 @@ mod tests { #[actix::test] async fn node_dkg_fold_compute_error_emits_none_aggregation_result() -> Result<()> { let (bus, _rng, _seed, _params, _crp, _errors, history) = get_common_setup(None)?; - let mut aggregator = NodeProofAggregator::new(&bus, test_signer(), None); + let mut aggregator = NodeProofAggregator::new(&bus, test_signer(), HashMap::new()); let e3_id = E3id::new("42", 1); let correlation_id = CorrelationId::new(); @@ -663,7 +680,7 @@ mod tests { #[actix::test] async fn early_inner_proof_is_prebuffered_until_collection_starts() -> Result<()> { let (bus, _rng, _seed, _params, _crp, _errors, history) = get_common_setup(None)?; - let mut aggregator = NodeProofAggregator::new(&bus, test_signer(), None); + let mut aggregator = NodeProofAggregator::new(&bus, test_signer(), HashMap::new()); let e3_id = E3id::new("43", 1); let early_proof = dummy_proof(10); From a03b42e082fca0ec76c501317447b070ab620343 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sun, 24 May 2026 12:56:01 +0200 Subject: [PATCH 52/54] coderabbit nits --- crates/multithread/src/multithread.rs | 40 ++++------ docs/pages/internals/dkg.mdx | 11 ++- examples/CRISP/client/src/utils/methods.ts | 5 +- examples/CRISP/scripts/lib/dev_config.sh | 4 +- .../scripts/benchmarkGasFromRaw.ts | 74 +++++++++++++++---- 5 files changed, 86 insertions(+), 48 deletions(-) diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index d3f1cce85..43d24bbbe 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -534,32 +534,24 @@ fn handle_trbfv_request( request: ComputeRequest, id: u8, ) -> (Result, Duration) { - // Hold one RNG lock for the whole TrBFV job so overlapping pool tasks do not interleave draws. - let mut rng_guard = rng.lock().expect("SharedRng mutex poisoned"); - let rng_mut = &mut *rng_guard; - match trbfv_req { - TrBFVRequest::GenPkShareAndSkSss(req) => { - timefunc( - "gen_pk_share_and_sk_sss", - id, - || match gen_pk_share_and_sk_sss(rng_mut, &cipher, req) { - Ok(o) => Ok(ComputeResponse::trbfv( - TrBFVResponse::GenPkShareAndSkSss(o), - request.correlation_id, - request.e3_id, - )), - Err(e) => Err(ComputeRequestError::new( - ComputeRequestErrorKind::TrBFV(TrBFVError::GenPkShareAndSkSss( - e.to_string(), - )), - request, - )), - }, - ) - } + TrBFVRequest::GenPkShareAndSkSss(req) => timefunc("gen_pk_share_and_sk_sss", id, || { + let mut rng_guard = rng.lock().expect("SharedRng mutex poisoned"); + match gen_pk_share_and_sk_sss(&mut *rng_guard, &cipher, req) { + Ok(o) => Ok(ComputeResponse::trbfv( + TrBFVResponse::GenPkShareAndSkSss(o), + request.correlation_id, + request.e3_id, + )), + Err(e) => Err(ComputeRequestError::new( + ComputeRequestErrorKind::TrBFV(TrBFVError::GenPkShareAndSkSss(e.to_string())), + request, + )), + } + }), TrBFVRequest::GenEsiSss(req) => timefunc("gen_esi_sss", id, || { - match gen_esi_sss(rng_mut, &cipher, req) { + let mut rng_guard = rng.lock().expect("SharedRng mutex poisoned"); + match gen_esi_sss(&mut *rng_guard, &cipher, req) { Ok(o) => Ok(ComputeResponse::trbfv( TrBFVResponse::GenEsiSss(o), request.correlation_id, diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index 33408a0eb..49e86a197 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -567,10 +567,13 @@ Governance can change it with `setAccusationVoteValidity(uint256)`. Two notable - **Shorter window**: tighter freshness — useful if stolen-signature replay becomes a concern, at the cost of giving on-chain submitters less slack to land the `proposeSlash` transaction after off-chain quorum reaches `M`. -- **Zero**: effectively disables slashing submission. Every new vote's `deadline` collapses to - "now", which the on-chain check rejects immediately. This is the emergency stop — useful while - investigating a runaway accusation but should be restored quickly to keep the slashing path - operational. +- **Zero**: practically disables slashing — `accusationVoteValidity = 0` is a zero-second window, so + every new vote's `deadline` is stamped as `now` (no slack beyond the current block time). + Off-chain nodes still gossip votes; `proposeSlash` succeeds only if `SlashingManager` sees at + least **M** agreeing `AccusationVote` signatures and passes `block.timestamp <= deadline`. That + comparison is inclusive, so a vote whose `deadline` equals the current block time can still land + if `proposeSlash` is included in the **same block** — zero is an emergency brake, not a guaranteed + hard stop. Restore a non-zero window quickly to keep the slashing path operational. In-flight ciphernodes do not pick up the new value until restart. After a non-trivial shortening (e.g. 30 min → 1 min) operators should coordinate a rolling restart, otherwise live nodes will keep diff --git a/examples/CRISP/client/src/utils/methods.ts b/examples/CRISP/client/src/utils/methods.ts index 219e82bf0..2af45c2ca 100644 --- a/examples/CRISP/client/src/utils/methods.ts +++ b/examples/CRISP/client/src/utils/methods.ts @@ -23,8 +23,9 @@ export const convertTimestampToDate = (timestamp: number, secondsToAdd: number = } export const getChain = (): Chain => { - const chainId = import.meta.env.VITE_CHAIN_ID - if (chainId === '31337' || chainId === 31337) return anvil + const chainId = Number.parseInt(String(import.meta.env.VITE_CHAIN_ID ?? ''), 10) + if (chainId === anvil.id) return anvil + if (chainId === sepolia.id) return sepolia return import.meta.env.DEV ? anvil : sepolia } diff --git a/examples/CRISP/scripts/lib/dev_config.sh b/examples/CRISP/scripts/lib/dev_config.sh index 0ad25b4b8..6b6411489 100644 --- a/examples/CRISP/scripts/lib/dev_config.sh +++ b/examples/CRISP/scripts/lib/dev_config.sh @@ -76,8 +76,8 @@ compile_enclave_dkg_circuits_if_needed() { echo "Building enclave DKG circuits (preset=${CRISP_BFV_PRESET})..." ( - cd "${REPO_ROOT}" - pnpm build:circuits --preset "${CRISP_BFV_PRESET}" --skip-if-built + cd "${REPO_ROOT}" && + pnpm build:circuits --preset "${CRISP_BFV_PRESET}" --skip-if-built ) } diff --git a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts index ac131125c..4bdce87c1 100644 --- a/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts +++ b/packages/enclave-contracts/scripts/benchmarkGasFromRaw.ts @@ -4,7 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. import { network } from "hardhat"; -import { execSync } from "node:child_process"; +import { execFileSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; @@ -26,7 +26,35 @@ const COMMITTED_HONK_DIR = path.join( "packages/enclave-contracts/contracts/verifiers/bfv/honk", ); -function readBenchmarkPreset(): string { +function presetFromFoldedArtifact(artifact: unknown): string | undefined { + if (!artifact || typeof artifact !== "object") { + return undefined; + } + const doc = artifact as Record; + const pick = (value: unknown): string | undefined => { + if (typeof value !== "string") return undefined; + const trimmed = value.trim(); + return trimmed.length > 0 ? trimmed : undefined; + }; + + const direct = pick(doc.preset) ?? pick(doc.bfv_preset_subdir); + if (direct) return direct; + + const benchmarkConfig = doc.benchmark_config; + if (benchmarkConfig && typeof benchmarkConfig === "object") { + const cfg = benchmarkConfig as Record; + const fromConfig = pick(cfg.bfv_preset_subdir) ?? pick(cfg.preset); + if (fromConfig) return fromConfig; + } + + return undefined; +} + +/** Prefer preset embedded in replayed folded/summary JSON, then env / active preset. */ +function readBenchmarkPreset(foldedArtifact?: unknown): string { + const fromArtifact = presetFromFoldedArtifact(foldedArtifact); + if (fromArtifact) return fromArtifact; + const fromEnv = process.env.BENCHMARK_PRESET?.trim(); if (fromEnv) return fromEnv; const activePath = path.join(REPO_ROOT, "circuits/bin/.active-preset.json"); @@ -56,19 +84,23 @@ function ensureHonkVerifierContractDir(preset: string): string { console.log( `[benchmarkGasFromRaw] Generating ${preset} Honk verifiers into ${benchDir}...`, ); - execSync( + execFileSync( + "pnpm", [ - "pnpm generate:verifiers", - "--circuits dkg_aggregator,decryption_aggregator", + "generate:verifiers", + "--circuits", + "dkg_aggregator,decryption_aggregator", "--no-compile", "--write", - `--preset ${preset}`, - `--output-dir ${benchDir}`, - ].join(" "), + "--preset", + preset, + "--output-dir", + benchDir, + ], { cwd: REPO_ROOT, stdio: "inherit" }, ); // Hardhat does not pick up freshly written .sol under honk/.benchmark/ until compile. - execSync("pnpm hardhat compile", { + execFileSync("pnpm", ["hardhat", "compile"], { cwd: path.join(REPO_ROOT, "packages/enclave-contracts"), stdio: "inherit", }); @@ -196,6 +228,7 @@ async function main() { let dkgPublicHex: string | undefined; let decProofHex: string | undefined; let decPublicHex: string | undefined; + let foldedDoc: unknown; if (foldedPath && fs.existsSync(foldedPath)) { const raw = fs.readFileSync(foldedPath, "utf8").trim(); @@ -204,12 +237,21 @@ async function main() { `[benchmarkGasFromRaw] ${foldedPath} is empty — integration test likely failed before exporting folded proofs`, ); } else { - const folded = JSON.parse(raw); - const artifacts = folded?.folded_artifacts ?? folded; - dkgProofHex = artifacts?.dkg_aggregator?.proof_hex; - dkgPublicHex = artifacts?.dkg_aggregator?.public_inputs_hex; - decProofHex = artifacts?.decryption_aggregator?.proof_hex; - decPublicHex = artifacts?.decryption_aggregator?.public_inputs_hex; + foldedDoc = JSON.parse(raw); + const artifacts = + (foldedDoc as { folded_artifacts?: unknown }).folded_artifacts ?? + foldedDoc; + const proofBundle = artifacts as { + dkg_aggregator?: { proof_hex?: string; public_inputs_hex?: string }; + decryption_aggregator?: { + proof_hex?: string; + public_inputs_hex?: string; + }; + }; + dkgProofHex = proofBundle?.dkg_aggregator?.proof_hex; + dkgPublicHex = proofBundle?.dkg_aggregator?.public_inputs_hex; + decProofHex = proofBundle?.decryption_aggregator?.proof_hex; + decPublicHex = proofBundle?.decryption_aggregator?.public_inputs_hex; } } else { const dkgRaw = findRawJson(rawDir, "threshold_pk_aggregation"); @@ -282,7 +324,7 @@ async function main() { const abiCoder = ethers.AbiCoder.defaultAbiCoder(); - const benchmarkPreset = readBenchmarkPreset(); + const benchmarkPreset = readBenchmarkPreset(foldedDoc); const honkDir = ensureHonkVerifierContractDir(benchmarkPreset); if (benchmarkPreset !== CANONICAL_BFV_PRESET) { console.log( From 461c5d66f7b01df52a26285da546e1daff533877 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sun, 24 May 2026 15:07:43 +0200 Subject: [PATCH 53/54] audit review impls --- crates/evm/src/dkg_attestation_bundle.rs | 2 +- crates/evm/src/evm_chain_gateway.rs | 2 - crates/evm/src/slashing_manager_sol_writer.rs | 78 +++++- .../src/actors/accusation_manager.rs | 20 +- .../src/actors/node_proof_aggregator.rs | 39 ++- crates/zk-prover/src/node_fold_public.rs | 38 +++ .../contracts/Enclave.sol/Enclave.json | 2 +- .../IBondingRegistry.json | 2 +- .../ICiphernodeRegistry.json | 135 +++++++++- .../interfaces/IEnclave.sol/IEnclave.json | 2 +- .../ISlashingManager.json | 49 +++- .../CiphernodeRegistryOwnable.json | 172 +++++++++++- .../interfaces/ICiphernodeRegistry.sol | 49 ++++ .../contracts/interfaces/ISlashingManager.sol | 29 ++ .../contracts/lib/CommitteeHashLib.sol | 8 +- .../registry/CiphernodeRegistryOwnable.sol | 58 ++++ .../contracts/slashing/SlashingManager.sol | 41 +++ .../contracts/test/MockCiphernodeRegistry.sol | 68 ++++- .../verifiers/DkgFoldAttestationVerifier.sol | 81 ++++-- .../test/BfvVkBindingIntegration.spec.ts | 17 +- .../enclave-contracts/test/Enclave.spec.ts | 129 +-------- ...iphernodeRegistryVerifierLifecycle.spec.ts | 167 ++++++++++++ .../DkgFoldAttestationVerifier.spec.ts | 252 ++++++++++++++++++ .../test/Slashing/SlashingManager.spec.ts | 239 +++++++++++------ .../test/fixtures/dkgAttestation.ts | 173 ++++++++++++ .../enclave-contracts/test/fixtures/index.ts | 1 + 26 files changed, 1554 insertions(+), 299 deletions(-) create mode 100644 packages/enclave-contracts/test/Registry/CiphernodeRegistryVerifierLifecycle.spec.ts create mode 100644 packages/enclave-contracts/test/Registry/DkgFoldAttestationVerifier.spec.ts create mode 100644 packages/enclave-contracts/test/fixtures/dkgAttestation.ts diff --git a/crates/evm/src/dkg_attestation_bundle.rs b/crates/evm/src/dkg_attestation_bundle.rs index 43c849069..6a2adc0df 100644 --- a/crates/evm/src/dkg_attestation_bundle.rs +++ b/crates/evm/src/dkg_attestation_bundle.rs @@ -79,7 +79,7 @@ pub fn encode_dkg_attestation_bundle( mod tests { use super::*; use alloy::primitives::keccak256; - use alloy::signers::{local::PrivateKeySigner, SignerSync}; + use alloy::signers::local::PrivateKeySigner; use e3_events::{DkgFoldAggCommits, DkgFoldAttestationPayload, E3id}; #[test] diff --git a/crates/evm/src/evm_chain_gateway.rs b/crates/evm/src/evm_chain_gateway.rs index e192883a2..e361a2c00 100644 --- a/crates/evm/src/evm_chain_gateway.rs +++ b/crates/evm/src/evm_chain_gateway.rs @@ -246,8 +246,6 @@ impl Handler for EvmChainGateway { #[cfg(test)] mod tests { - use std::time::Duration; - use crate::EvmEvent; use super::*; diff --git a/crates/evm/src/slashing_manager_sol_writer.rs b/crates/evm/src/slashing_manager_sol_writer.rs index e979422da..6bacf0867 100644 --- a/crates/evm/src/slashing_manager_sol_writer.rs +++ b/crates/evm/src/slashing_manager_sol_writer.rs @@ -4,8 +4,10 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -//! Subscribes to `AccusationQuorumReached` events and submits `proposeSlash` -//! transactions on the SlashingManager contract with committee attestation evidence. +//! Subscribes to `AccusationQuorumReached` events and submits committee-attested +//! slash proposals on the SlashingManager contract. Prefers party-attributed +//! `proposeSlashByDkgParty` when DKG anchors resolve, and falls back to +//! operator-attributed `proposeSlash` otherwise. use crate::error_decoder::format_evm_error; use crate::helpers::EthProvider; @@ -193,7 +195,7 @@ impl Handler } /// Encode `AccusationQuorumReached` into the attestation evidence format expected -/// by `SlashingManager.proposeSlash()`: +/// by both `SlashingManager.proposeSlash()` and `SlashingManager.proposeSlashByDkgParty()`: /// `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, uint256 deadline, bytes[] signatures)` /// /// Voters are sorted ascending by address to satisfy the contract's duplicate-prevention @@ -258,10 +260,20 @@ async fn submit_slash_proposal( } }; + let party_id = + resolve_party_id_for_operator(provider.clone(), contract_address, e3_id, operator) + .await + .ok() + .flatten(); + send_tx_with_retry("proposeSlash", &[], || { - info!("proposeSlash() e3_id={:?} operator={:?}", e3_id, operator); + info!( + "proposeSlash() e3_id={:?} operator={:?} party_id={:?}", + e3_id, operator, party_id + ); let proof = Bytes::from(proof_data.clone()); let provider = provider.clone(); + let party_id = party_id; async move { let from_address = provider.provider().default_signer_address(); @@ -271,12 +283,62 @@ async fn submit_slash_proposal( .pending() .await?; let contract = ISlashingManager::new(contract_address, provider.provider()); - let builder = contract - .proposeSlash(e3_id, operator, proof) - .nonce(current_nonce); - let receipt = builder.send().await?.get_receipt().await?; + let receipt = if let Some(pid) = party_id { + contract + .proposeSlashByDkgParty(e3_id, pid, proof) + .nonce(current_nonce) + .send() + .await? + .get_receipt() + .await? + } else { + contract + .proposeSlash(e3_id, operator, proof) + .nonce(current_nonce) + .send() + .await? + .get_receipt() + .await? + }; Ok(receipt) } }) .await } + +async fn resolve_party_id_for_operator( + provider: EthProvider

, + contract_address: Address, + e3_id: U256, + operator: Address, +) -> Result> { + sol! { + #[sol(rpc)] + interface IRegistryDkgView { + function getDkgAnchors(uint256 e3Id) + external + view + returns (uint256[] memory partyIds, bytes32[] memory, bytes32[] memory); + function canonicalCommitteeNodeAt(uint256 e3Id, uint256 partyId) external view returns (address); + } + } + + let slashing = ISlashingManager::new(contract_address, provider.provider()); + let registry = slashing.ciphernodeRegistry().call().await?; + if registry == Address::ZERO { + return Ok(None); + } + + let registry_view = IRegistryDkgView::new(registry, provider.provider()); + let anchors = registry_view.getDkgAnchors(e3_id).call().await?; + for pid in anchors.partyIds { + let node = registry_view + .canonicalCommitteeNodeAt(e3_id, pid) + .call() + .await?; + if node == operator { + return Ok(Some(pid)); + } + } + Ok(None) +} diff --git a/crates/zk-prover/src/actors/accusation_manager.rs b/crates/zk-prover/src/actors/accusation_manager.rs index 07bb2492f..40f61d040 100644 --- a/crates/zk-prover/src/actors/accusation_manager.rs +++ b/crates/zk-prover/src/actors/accusation_manager.rs @@ -332,12 +332,13 @@ impl AccusationManager { // ─── Signing / Verification ────────────────────────────────────────── - fn sign_accusation_digest(&self, accusation: &ProofFailureAccusation) -> Vec { + fn sign_accusation_digest( + &self, + accusation: &ProofFailureAccusation, + ) -> Result, alloy::signers::Error> { let digest = Self::accusation_digest(accusation); - self.signer - .sign_message_sync(&digest) - .map(|sig| sig.as_bytes().to_vec()) - .unwrap_or_default() + let sig = self.signer.sign_message_sync(&digest)?; + Ok(sig.as_bytes().to_vec()) } /// Structured digest for ECDSA signing of accusations. @@ -662,7 +663,14 @@ impl AccusationManager { signed_payload: forwarded_payload, signature: ArcBytes::default(), }; - accusation.signature = ArcBytes::from_bytes(&self.sign_accusation_digest(&accusation)); + match self.sign_accusation_digest(&accusation) { + Ok(sig) => accusation.signature = ArcBytes::from_bytes(&sig), + Err(err) => { + error!("Failed to sign ProofFailureAccusation: {err}"); + self.accused_proofs.remove(&key); + return; + } + } let accusation_id = Self::accusation_id(&accusation); diff --git a/crates/zk-prover/src/actors/node_proof_aggregator.rs b/crates/zk-prover/src/actors/node_proof_aggregator.rs index 6fa6e0c6f..a45ca6a6a 100644 --- a/crates/zk-prover/src/actors/node_proof_aggregator.rs +++ b/crates/zk-prover/src/actors/node_proof_aggregator.rs @@ -15,9 +15,10 @@ use alloy::signers::local::PrivateKeySigner; use e3_events::{ BusHandle, ComputeRequest, ComputeRequestError, ComputeResponse, ComputeResponseKind, CorrelationId, DKGInnerProofReady, DKGRecursiveAggregationComplete, DkgFoldAttestationPayload, - E3id, EnclaveEvent, EnclaveEventData, EventContext, EventPublisher, EventSubscriber, EventType, - NodeDkgFoldRequest, Proof, Sequenced, ShareEncryptionProofRequest, SignedDkgFoldAttestation, - ThresholdSharePending, TypedEvent, ZkRequest, ZkResponse, + E3Failed, E3Stage, E3id, EnclaveEvent, EnclaveEventData, EventContext, EventPublisher, + EventSubscriber, EventType, FailureReason, NodeDkgFoldRequest, Proof, Sequenced, + ShareEncryptionProofRequest, SignedDkgFoldAttestation, ThresholdSharePending, TypedEvent, + ZkRequest, ZkResponse, }; use e3_fhe_params::build_pair_for_preset; use tracing::{error, info, warn}; @@ -388,19 +389,18 @@ impl NodeProofAggregator { error!( e3_id = %e3_id, party_id, - "NodeDkgFold succeeded but fold attestation missing — publishing without proof" + "NodeDkgFold succeeded but fold attestation missing — failing E3" ); if let Err(err) = self.bus.publish( - DKGRecursiveAggregationComplete { + E3Failed { e3_id: e3_id.clone(), - party_id, - aggregated_proof: None, - fold_attestation: None, + failed_at_stage: E3Stage::CommitteeFinalized, + reason: FailureReason::DKGInvalidShares, }, state.last_ec, ) { error!( - "NodeProofAggregator: failed to publish DKGRecursiveAggregationComplete for E3 {}: {err}", + "NodeProofAggregator: failed to publish E3Failed for E3 {}: {err}", e3_id ); } @@ -537,22 +537,21 @@ impl NodeProofAggregator { ); let state = self.states.remove(&e3_id); warn!( - "NodeProofAggregator: E3 {} NodeDkgFold failed — publishing DKGRecursiveAggregationComplete(None)", + "NodeProofAggregator: E3 {} NodeDkgFold failed — publishing E3Failed", e3_id ); - if let Some(state) = state { + if let Some(_state) = state { if let Err(err) = self.bus.publish( - DKGRecursiveAggregationComplete { + E3Failed { e3_id: e3_id.clone(), - party_id: state.meta.party_id, - aggregated_proof: None, - fold_attestation: None, + failed_at_stage: E3Stage::CommitteeFinalized, + reason: FailureReason::DKGInvalidShares, }, ec, ) { error!( - "NodeProofAggregator: failed to publish DKGRecursiveAggregationComplete(None) for E3 {}: {err}", + "NodeProofAggregator: failed to publish E3Failed for E3 {}: {err}", e3_id ); } @@ -596,7 +595,7 @@ mod tests { } #[actix::test] - async fn node_dkg_fold_compute_error_emits_none_aggregation_result() -> Result<()> { + async fn node_dkg_fold_compute_error_emits_e3_failed() -> Result<()> { let (bus, _rng, _seed, _params, _crp, _errors, history) = get_common_setup(None)?; let mut aggregator = NodeProofAggregator::new(&bus, test_signer(), HashMap::new()); let e3_id = E3id::new("42", 1); @@ -666,10 +665,10 @@ mod tests { let event = next_event(&history).await?; assert!(matches!( event.into_data(), - EnclaveEventData::DKGRecursiveAggregationComplete(data) + EnclaveEventData::E3Failed(data) if data.e3_id == e3_id - && data.party_id == 7 - && data.aggregated_proof.is_none() + && data.failed_at_stage == E3Stage::CommitteeFinalized + && data.reason == FailureReason::DKGInvalidShares )); assert!(!aggregator.states.contains_key(&e3_id)); assert!(aggregator.fold_correlation.is_empty()); diff --git a/crates/zk-prover/src/node_fold_public.rs b/crates/zk-prover/src/node_fold_public.rs index f94e62ff8..6f83f8723 100644 --- a/crates/zk-prover/src/node_fold_public.rs +++ b/crates/zk-prover/src/node_fold_public.rs @@ -69,3 +69,41 @@ pub fn extract_node_fold_agg_commits( }, )) } + +#[cfg(test)] +mod tests { + use super::*; + use e3_utils::ArcBytes; + + #[test] + fn extracts_expected_fields_from_golden_layout_vector() { + // Golden layout vector: verifies positional extraction + // (party_id at index 0, sk/esm commits at tail). + let n = 3usize; + let h = 3usize; + let l = 2usize; + let field_count = node_fold_public_field_count(n, h, l); + + let mut fields = vec![[0u8; 32]; field_count]; + fields[0][31] = 2; // party_id = 2 + fields[field_count - 2] = [0x11; 32]; + fields[field_count - 1] = [0x22; 32]; + + let mut public_signals = Vec::with_capacity(field_count * 32); + for f in fields { + public_signals.extend_from_slice(&f); + } + + let proof = Proof::new( + CircuitName::NodeFold, + ArcBytes::from_bytes(&[]), + ArcBytes::from_bytes(&public_signals), + ); + + let (party_id, commits) = + extract_node_fold_agg_commits(&proof, n, h, l).expect("extract should succeed"); + assert_eq!(party_id, 2); + assert_eq!(commits.sk_agg_commit, [0x11; 32]); + assert_eq!(commits.esm_agg_commit, [0x22; 32]); + } +} diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index b54830663..448c3ab8f 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -3159,5 +3159,5 @@ }, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" + "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 894fc9482..b03dc84e7 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -1062,5 +1062,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" + "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 9418eac6c..0e99886ec 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -3,6 +3,43 @@ "contractName": "ICiphernodeRegistry", "sourceName": "contracts/interfaces/ICiphernodeRegistry.sol", "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "pending", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "provided", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nowAt", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityTimelockActive", + "type": "error" + }, + { + "inputs": [], + "name": "AccusationVoteValidityZeroRequiresTimelock", + "type": "error" + }, { "inputs": [], "name": "AttestationBindingCountMismatch", @@ -115,6 +152,11 @@ "name": "InvalidTicketNumber", "type": "error" }, + { + "inputs": [], + "name": "NoPendingAccusationVoteValidityUpdate", + "type": "error" + }, { "inputs": [], "name": "NoPendingVerifierUpdate", @@ -249,6 +291,38 @@ "name": "ZeroAddress", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityProposalCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityProposed", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -631,6 +705,19 @@ "name": "TicketSubmitted", "type": "event" }, + { + "inputs": [], + "name": "accusationVoteValidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -644,6 +731,13 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "cancelAccusationVoteValidityProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -668,6 +762,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "commitAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1002,6 +1109,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "proposeAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1109,6 +1229,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "setAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1199,5 +1332,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" + "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index 2537b9f11..356890b82 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2427,5 +2427,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" + "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index a3b619af7..d839196ce 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -108,6 +108,11 @@ "name": "OperatorUnderSlash", "type": "error" }, + { + "inputs": [], + "name": "PartyIdNotInDkgAnchors", + "type": "error" + }, { "inputs": [], "name": "ProofIsValid", @@ -640,6 +645,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "ciphernodeRegistry", + "outputs": [ + { + "internalType": "contract ICiphernodeRegistry", + "name": "registry", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -964,6 +982,35 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "e3Id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "partyId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + } + ], + "name": "proposeSlashByDkgParty", + "outputs": [ + { + "internalType": "uint256", + "name": "proposalId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1186,5 +1233,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" + "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index e1f4ae4f1..183629e79 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -8,6 +8,43 @@ "stateMutability": "nonpayable", "type": "constructor" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "pending", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "provided", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nowAt", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityTimelockActive", + "type": "error" + }, + { + "inputs": [], + "name": "AccusationVoteValidityZeroRequiresTimelock", + "type": "error" + }, { "inputs": [], "name": "AttestationBindingCountMismatch", @@ -130,6 +167,11 @@ "name": "InvalidTicketNumber", "type": "error" }, + { + "inputs": [], + "name": "NoPendingAccusationVoteValidityUpdate", + "type": "error" + }, { "inputs": [], "name": "NoPendingVerifierUpdate", @@ -307,6 +349,38 @@ "name": "ZeroAddress", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityProposalCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "accusationVoteValidity", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "readyAt", + "type": "uint256" + } + ], + "name": "AccusationVoteValidityProposed", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -779,6 +853,19 @@ "name": "TicketSubmitted", "type": "event" }, + { + "inputs": [], + "name": "ACCUSATION_VOTE_VALIDITY_TIMELOCK", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "DEFAULT_ACCUSATION_VOTE_VALIDITY", @@ -903,6 +990,13 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "cancelAccusationVoteValidityProposal", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "cancelDkgFoldAttestationVerifierProposal", @@ -990,6 +1084,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "commitAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1407,6 +1514,32 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "pendingAccusationVoteValidity", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingAccusationVoteValidityAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "pendingDkgFoldAttestationVerifier", @@ -1446,6 +1579,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_accusationVoteValidity", + "type": "uint256" + } + ], + "name": "proposeAccusationVoteValidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1779,30 +1925,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b614afb806100d96000396000f3fe608060405234801561001057600080fd5b50600436106102b45760003560e01c806301ffc9a7146102b9578063096b810a146102e1578063099a161a146102f65780630b45f8e9146103175780630b88a79a1461032a5780630bbfade7146103335780630f3e34121461034657806317d61120146103595780631e08d0e81461037a5780632800d82914610382578063291a691b146103955780632e7b716d146103a8578063323beaa5146103bb5780634d6861a6146103ce57806350e6d94c146103e157806350e6fad3146104045780635d5047761461040e5780635efb633c1461042157806362cc89a81461042a5780636c120a951461044a57806370e36bbe1461045d578063715018a61461047057806379ba5097146104785780637c92f5241461048057806385814243146104ad5780638a78bb15146104c05780638cb89ecb146104d35780638d1ddfb1146104f35780638da5cb5b146105095780638e5ce3ad146105115780639015d371146105245780639a7a2ffc146105375780639f0f874a14610574578063a01649301461057d578063a8a4d69b1461059d578063acc52494146105b0578063b2d5d1ac146105c3578063b8ab4704146105cc578063bbe4b803146105ee578063bff232c1146105f8578063c2b40ae41461060b578063c3a0ec301461062b578063c8fe182d1461063c578063ca2869a014610644578063cd6dc68714610664578063cf90b6ed14610677578063da881e5a14610681578063dbb06c9314610694578063e30c3978146106a7578063e4be6e3d146106af578063e4d185db146106c2578063e59e4695146106d5578063e6745e13146106e8578063e82f3b70146106fb578063ebf0c7171461070e578063f165053614610716578063f2fde38b14610730578063f379b0df14610743578063f52fd8031461077d578063f6fc05d5146107ee575b600080fd5b6102cc6102c7366004613d99565b6107f7565b60405190151581526020015b60405180910390f35b6102f46102ef366004613dd8565b61082e565b005b610309610304366004613df5565b61096d565b6040519081526020016102d8565b6102f4610325366004613dd8565b6109a7565b61030961070881565b6102f4610341366004613e56565b610a8a565b6102f4610354366004613df5565b610d4b565b61036c610367366004613df5565b610dc6565b6040516102d8929190613f83565b610309600181565b610309610390366004613df5565b610f76565b6102cc6103a3366004613fb1565b610fc3565b6102cc6103b6366004613dd8565b6111a4565b6102f46103c9366004613dd8565b611255565b6102cc6103dc366004613df5565b61134f565b6102cc6103ef366004613dd8565b60066020526000908152604090205460ff1681565b6103096202a30081565b6102cc61041c366004613fee565b611390565b610309600f5481565b600d5461043d906001600160a01b031681565b6040516102d8919061401e565b6102f4610458366004613df5565b6113d5565b6102f461046b366004613dd8565b611400565b6102f4611477565b6102f461149b565b61049361048e366004614032565b6114da565b6040805192835263ffffffff9091166020830152016102d8565b60015461043d906001600160a01b031681565b6102f46104ce366004613dd8565b61167c565b6103096104e1366004613df5565b60096020526000908152604090205481565b600454600160281b900464ffffffffff16610309565b61043d6117c7565b600b5461043d906001600160a01b031681565b6102cc610532366004613dd8565b6117e2565b61055e610545366004613dd8565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102d8565b61030960035481565b61059061058b366004613df5565b611800565b6040516102d8919061406a565b6102cc6105ab366004613fee565b611899565b6102f46105be366004613dd8565b6118de565b610309600e5481565b6105df6105da366004613df5565b611975565b6040516102d89392919061407d565b6103096210000081565b6102f4610606366004613dd8565b611ac8565b610309610619366004613df5565b60086020526000908152604090205481565b6001546001600160a01b031661043d565b6102f4611b41565b610309610652366004613df5565b60009081526008602052604090205490565b6102f46106723660046140c0565b611bac565b61030962093a8081565b6102cc61068f366004613df5565b611d34565b60005461043d906001600160a01b031681565b61043d612029565b600c5461043d906001600160a01b031681565b61043d6106d03660046140ec565b612034565b6102f46106e3366004613dd8565b6120d5565b6102f46106f63660046140ec565b61214e565b610309610709366004613df5565b612317565b610309612349565b61071e601481565b60405160ff90911681526020016102d8565b6102f461073e366004613dd8565b61235c565b60045461075f9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102d8565b6107bf61078b366004613df5565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102d8949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61030960025481565b60006001600160e01b03198216635a23ad1360e01b148061082857506001600160e01b031982166301ffc9a760e01b145b92915050565b6108366117c7565b6001600160a01b0316336001600160a01b0316148061085f57506001546001600160a01b031633145b61087c57604051632864c4e160e01b815260040160405180910390fd5b610885816117e2565b81906108ae576040516381e5828960e01b81526004016108a5919061401e565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff16906108dd90600490836123cd565b6001600160a01b0382166000908152600660205260408120805460ff19169055600280549161090b83614124565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d59261096192869291600160281b900464ffffffffff169061413b565b60405180910390a25050565b6000818152600a60205260408120600481015461099d576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b6109af612619565b600c546001600160a01b0316156109d85760405162035f5560e61b815260040160405180910390fd5b6001600160a01b0381166109ff5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610a6557600d80546001600160a01b031981169091556000600e8190556040516001600160a01b03909216918291600080516020614a6f83398151915291a2505b6040516001600160a01b03821690600080516020614a8f83398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610ab057610ab061415c565b14610ace57604051634f4b461f60e11b815260040160405180910390fd5b600481015415610af15760405163632a22bb60e01b815260040160405180910390fd5b85610b0f57604051636caad1ed60e11b815260040160405180910390fd5b6000610b7682600601805480602002602001604051908101604052809291908181526020018280548015610b6c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b4e575b505050505061264d565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610bdf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c079190810190614310565b9050806101c0015115610c9557610c958b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610c8557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c67575b50505050508c878d8d8d8d612759565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610cc7908e908c90600401614474565b600060405180830381600087803b158015610ce157600080fd5b505af1158015610cf5573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610d36969594939291906144eb565b60405180910390a25050505050505050505050565b610d53612619565b60018110158015610d67575062093a808111155b8190610d895760405163028237cd60e61b81526004016108a591815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab7906020015b60405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610dfd57610dfd614172565b604051908082528060200260200182016040528015610e26578160200160208202803683370190505b509450806001600160401b03811115610e4157610e41614172565b604051908082528060200260200182016040528015610e6a578160200160208202803683370190505b5093506000805b83811015610f6c576000856006018281548110610e9057610e90614539565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610ed857610ed861415c565b03610f635780888481518110610ef057610ef0614539565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610f4a57610f4a614539565b602090810291909101015282610f5f8161454f565b9350505b50600101610e71565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610f9b57610f9b61415c565b03610fb957604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610fef5760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff1660038111156110145761101461415c565b14611032576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa15801561107c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a09190614568565b9050806110b36040860160208701614595565b63ffffffff1611156110cb6040860160208701614595565b8290916110f9576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016108a5565b5050815460ff19166001908117835582018590554260028301819055600354611121916145b0565b600383015561113560058301856002613ca7565b5061113e612349565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611190928a928a92916145c3565b60405180910390a250600195945050505050565b60006111af826117e2565b6111bb57506000919050565b6001546001600160a01b03166111e4576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d79061121490859060040161401e565b602060405180830381865afa158015611231573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108289190614614565b61125d612619565b600d546001600160a01b0316806112875760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b03828116908216146112c857604051630d77758160e31b81526001600160a01b039283166004820152911660248201526044016108a5565b505060006202a300600e546112dd91906145b0565b9050804281811015611304576040516337c8270b60e01b81526004016108a5929190614474565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e819055604051600080516020614a8f8339815191529190a2505050565b6000818152600a602052604081206001815460ff1660038111156113755761137561415c565b146113835750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156113cd576113cd61415c565b149392505050565b6113dd612619565b600f819055604051818152600080516020614aaf83398151915290602001610dbb565b611408612619565b6001600160a01b03811661142f5760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61147f612619565b6040516001623f026d60e01b0319815260040160405180910390fd5b33806114a5612029565b6001600160a01b0316146114ce578060405163118cdaa760e01b81526004016108a5919061401e565b6114d78161280f565b50565b600b5460009081906001600160a01b0316331461150a5760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff1660038111156115305761153061415c565b1461154e57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561158f5761158f61415c565b1461159f57600b01549150611674565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b82018054916115d483614124565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161161c929190614474565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6116846117c7565b6001600160a01b0316336001600160a01b031614806116ad57506001546001600160a01b031633145b6116ca57604051632864c4e160e01b815260040160405180910390fd5b6116d3816117e2565b6114d757600454600160281b900464ffffffffff1662100000811061170b576040516335b4ac3f60e01b815260040160405180910390fd5b61171f60046001600160a01b038416612836565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916117718361454f565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539261096192869291600160281b900464ffffffffff169061413b565b6000806117d26129b1565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a60205260409020600481015460609190611834576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561188c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161186e575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156118d5576118d561415c565b14159392505050565b6118e6612619565b6001600160a01b03811661190d5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611961906202a300906145b0565b60405190815260200160405180910390a250565b600081815260096020526040902054606090819081906119a8576040516322e679e360e11b815260040160405180910390fd5b600084815260106020908152604080832060118352818420601284529382902081548351818602810186019094528084529194939092918591830182828015611a1057602002820191906000526020600020905b8154815260200190600101908083116119fc575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611a6257602002820191906000526020600020905b815481526020019060010190808311611a4e575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611ab457602002820191906000526020600020905b815481526020019060010190808311611aa0575b505050505090509250925092509193909250565b611ad0612619565b6001600160a01b038116611af75760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611b49612619565b600d546001600160a01b031680611b735760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b03831691600080516020614a6f83398151915291a250565b6000611bb66129d5565b805490915060ff600160401b82041615906001600160401b0316600081158015611bdd5750825b90506000826001600160401b03166001148015611bf95750303b155b905081158015611c07575080155b15611c255760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611c4e57845460ff60401b1916600160401b1785555b6001600160a01b038716611c755760405163d92e233d60e01b815260040160405180910390fd5b611c7e336129fe565b611c8a60046014612a0f565b611c9386610d4b565b610708600f819055604051908152600080516020614aaf8339815191529060200160405180910390a1611cc46117c7565b6001600160a01b0316876001600160a01b031614611ce557611ce58761280f565b8315611d2b57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611d5957611d5961415c565b03611d7757604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611d8f57611d8f61415c565b14611dad57604051631860f69960e31b815260040160405180910390fd5b80600301544211611dd157604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611ebd578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b158015611e9b57600080fd5b505af1158015611eaf573d6000803e3d6000fd5b506000979650505050505050565b611ec682612a4e565b815460ff191660021782556006820154600b83018190556000816001600160401b03811115611ef757611ef7614172565b604051908082528060200260200182016040528015611f20578160200160208202803683370190505b50905060005b82811015611f9557846009016000866006018381548110611f4957611f49614539565b60009182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611f8257611f82614539565b6020908102919091010152600101611f26565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611fdc57600080fd5b505af1158015611ff0573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e5856006018360405161119092919061462f565b6000806117d2612b7f565b6000828152600a602052604081206002815460ff16600381111561205a5761205a61415c565b1461207857604051634f4b461f60e11b815260040160405180910390fd5b600681015483908082106120a1576040516326c5c55b60e11b81526004016108a5929190614474565b50508060060183815481106120b8576120b8614539565b6000918252602090912001546001600160a01b0316949350505050565b6120dd612619565b6001600160a01b0381166121045760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156121735761217361415c565b0361219157604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156121a9576121a961415c565b146121c757604051631860f69960e31b815260040160405180910390fd5b80600301544211156121ec57604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff161561221f5760405163257309f160e11b815260040160405180910390fd5b612228336111a4565b6122455760405163149fbcfd60e11b815260040160405180910390fd5b612250338385612ba3565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff191660011790559091506122cc90839083612d7f565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd8584604051612309929190614474565b60405180910390a350505050565b60008181526009602052604090205480612344576040516322e679e360e11b815260040160405180910390fd5b919050565b600061235760046014612f8b565b905090565b612364612619565b600061236e612b7f565b80546001600160a01b0319166001600160a01b03841690811782559091506123946117c7565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614acf83398151915282106123e757600080fd5b825464ffffffffff600160281b9091048116908216811161240757600080fd5b8260005b8186600101600061241c8488612ff1565b64ffffffffff1681526020019081526020016000208190555060008160016124449190614642565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116124795750612611565b6001851660000361254557600061249a8361249588600161465b565b612ff1565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916124fc91600401614678565b602060405180830381865af4158015612519573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253d9190614568565b9350506125fd565b6000612556836124956001896146a9565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916125b891600401614678565b602060405180830381865af41580156125d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f99190614568565b9350505b50647fffffffff600194851c16930161240b565b505050505050565b336126226117c7565b6001600160a01b03161461264b573360405163118cdaa760e01b81526004016108a5919061401e565b565b80516000908161265e8260146146c6565b6001600160401b0381111561267557612675614172565b6040519080825280601f01601f19166020018201604052801561269f576020820181803683370190505b50905060005b828110156127495760008582815181106126c1576126c1614539565b602002602001015160601b905060008260146126dd91906146c6565b905060005b601481101561273b578281601481106126fd576126fd614539565b1a60f81b8561270c83856145b0565b8151811061271c5761271c614539565b60200101906001600160f81b031916908160001a9053506001016126e2565b5050508060010190506126a5565b5080516020909101209392505050565b8261277757604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b81526004016127b497969594939291906146dd565b602060405180830381865afa1580156127d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f59190614614565b506128038a8585858561300f565b50505050505050505050565b6000612819612b7f565b80546001600160a01b0319168155905061283282613150565b5050565b8154600160281b900464ffffffffff16600080516020614acf833981519152821061286057600080fd5b825464ffffffffff9081169082161061287857600080fd5b61288381600161465b565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b818560010160006128ba8487612ff1565b64ffffffffff16815260208101919091526040016000205560018316156129aa5760006128ec826124956001876146a9565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161294e91600401614678565b602060405180830381865af415801561296b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061298f9190614568565b647fffffffff600195861c16949093509190910190506128a9565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610828565b612a066131ac565b6114d7816131d1565b602060ff82161115612a2057600080fd5b612a31600160ff831681901b614729565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612b7a576000612a6b8260016145b0565b90505b82811015612b71576000846006018381548110612a8d57612a8d614539565b60009182526020822001546006870180546001600160a01b0390921693509084908110612abc57612abc614539565b6000918252602090912001546001600160a01b0390811691508216811015612b675780866006018581548110612af457612af4614539565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612b3857612b38614539565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612a6e565b50600101612a56565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612bc45760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612bed576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612c2491614729565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c919190614568565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0c9190614568565b905060008111612d2f5760405163aeaddff160e01b815260040160405180910390fd5b6000612d3b828461473c565b905060008111612d5e5760405163149fbcfd60e11b815260040160405180910390fd5b80861115611d2b5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612dff57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612f84565b60008087600901600085600081548110612e1b57612e1b614539565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612ea7576000896009016000878481548110612e6857612e68614539565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e9e578092508193505b50600101612e45565b50808610612ebc576000945050505050612f84565b600088600a016000868581548110612ed657612ed6614539565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612f1457612f1461415c565b021790555086848381548110612f2c57612f2c614539565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff1611612f9c57600080fd5b602060ff83161115612fad57600080fd5b8254600160281b900464ffffffffff1680612fcc60ff8516600261486e565b64ffffffffff161015612fde57600080fd5b612fe9848285613203565b949350505050565b60008161300560ff851663ffffffff614888565b612f84919061465b565b8061302d57604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661305657604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e9061309790309046908d908d908d908d908d906004016148af565b600060405180830381865afa1580156130b4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526130dc9190810190614980565b60008b815260106020908152604090912084519497509295509093506131059290860190613d49565b506000888152601160209081526040909120835161312592850190613d49565b506000888152601260209081526040909120825161314592840190613d49565b505050505050505050565b600061315a6129b1565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6131b46132bb565b61264b57604051631afcd79f60e31b815260040160405180910390fd5b6131d96131ac565b6001600160a01b0381166114ce576000604051631e4fbdf760e01b81526004016108a5919061401e565b6000602060ff8316111561321657600080fd5b8264ffffffffff166000036132355761322e826132d5565b9050612f84565b6000613242836001614642565b60ff166001600160401b0381111561325c5761325c614172565b604051908082528060200260200182016040528015613285578160200160208202803683370190505b5090506132948585858461392a565b808360ff16815181106132a9576132a9614539565b60200260200101519150509392505050565b60006132c56129d5565b54600160401b900460ff16919050565b60008160ff166000036132ea57506000919050565b8160ff1660010361331c57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361334e57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361338057507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036133b257507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036133e457507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361341657507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361344857507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361347a57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036134ac57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036134de57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361351057507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361354257507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361357457507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036135a657507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036135d857507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361360a57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361363c57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361366e57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036136a057507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036136d257507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361370457507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361373657507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361376857507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361379a57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036137cc57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036137fe57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361383057507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361386257507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361389457507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036138c657507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f036138f857507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036102b457507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff8316111561393b57600080fd5b60008364ffffffffff161161394f57600080fd5b600061395c6001856146a9565b9050600181166000036139b45784600101600061397a600084612ff1565b64ffffffffff16815260200190815260200160002054826000815181106139a3576139a3614539565b6020026020010181815250506139de565b6139be60006132d5565b826000815181106139d1576139d1614539565b6020026020010181815250505b60005b8360ff168160ff1610156126115760018216600003613ada5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110613a3457613a34614539565b60200260200101518152602001613a4a856132d5565b8152506040518263ffffffff1660e01b8152600401613a699190614678565b602060405180830381865af4158015613a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aaa9190614568565b83613ab6836001614642565b60ff1681518110613ac957613ac9614539565b602002602001018181525050613c94565b6000613ae7826001614642565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613b8c576000876001016000613b40856001613b2f9190614642565b60018864ffffffffff16901c612ff1565b64ffffffffff1681526020019081526020016000205490508085846001613b679190614642565b60ff1681518110613b7a57613b7a614539565b60200260200101818152505050613c92565b6000876001016000613ba58560018861249591906146a9565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613bfd57613bfd614539565b60200260200101518152506040518263ffffffff1660e01b8152600401613c249190614678565b602060405180830381865af4158015613c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c659190614568565b85613c71856001614642565b60ff1681518110613c8457613c84614539565b602002602001018181525050505b505b647fffffffff600192831c1691016139e1565b600183019183908215613d395791602002820160005b83821115613d0757833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613cbd565b8015613d375782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613d07565b505b50613d45929150613d84565b5090565b828054828255906000526020600020908101928215613d39579160200282015b82811115613d39578251825591602001919060010190613d69565b5b80821115613d455760008155600101613d85565b600060208284031215613dab57600080fd5b81356001600160e01b031981168114612f8457600080fd5b6001600160a01b03811681146114d757600080fd5b600060208284031215613dea57600080fd5b8135612f8481613dc3565b600060208284031215613e0757600080fd5b5035919050565b60008083601f840112613e2057600080fd5b5081356001600160401b03811115613e3757600080fd5b602083019150836020828501011115613e4f57600080fd5b9250929050565b60008060008060008060008060a0898b031215613e7257600080fd5b8835975060208901356001600160401b03811115613e8f57600080fd5b613e9b8b828c01613e0e565b9098509650506040890135945060608901356001600160401b03811115613ec157600080fd5b613ecd8b828c01613e0e565b90955093505060808901356001600160401b03811115613eec57600080fd5b613ef88b828c01613e0e565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b82811015613f475781516001600160a01b0316865260209586019590910190600101613f20565b5093949350505050565b600081518084526020840193506020830160005b82811015613f47578151865260209586019590910190600101613f65565b604081526000613f966040830185613f0c565b8281036020840152613fa88185613f51565b95945050505050565b600080600060808486031215613fc657600080fd5b833592506020840135915060808401851015613fe157600080fd5b6040840190509250925092565b6000806040838503121561400157600080fd5b82359150602083013561401381613dc3565b809150509250929050565b6001600160a01b0391909116815260200190565b60008060006060848603121561404757600080fd5b83359250602084013561405981613dc3565b929592945050506040919091013590565b602081526000612f846020830184613f0c565b6060815260006140906060830186613f51565b82810360208401526140a28186613f51565b905082810360408401526140b68185613f51565b9695505050505050565b600080604083850312156140d357600080fd5b82356140de81613dc3565b946020939093013593505050565b600080604083850312156140ff57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6000816141335761413361410e565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b03811182821017156141ab576141ab614172565b60405290565b604051601f8201601f191681016001600160401b03811182821017156141d9576141d9614172565b604052919050565b80516004811061234457600080fd5b600082601f83011261420157600080fd5b604080519081016001600160401b038111828210171561422357614223614172565b806040525080604084018581111561423a57600080fd5b845b8181101561425457805183526020928301920161423c565b509195945050505050565b805161234481613dc3565b805160ff8116811461234457600080fd5b600082601f83011261428c57600080fd5b81516001600160401b038111156142a5576142a5614172565b6142b8601f8201601f19166020016141b1565b8181528460208386010111156142cd57600080fd5b60005b828110156142ec576020818601810151838301820152016142d0565b506000918101602001919091529392505050565b8051801515811461234457600080fd5b60006020828403121561432257600080fd5b81516001600160401b0381111561433857600080fd5b8201610200818503121561434b57600080fd5b614353614188565b81518152614363602083016141e1565b60208201526040828101519082015261437f85606084016141f0565b606082015260a0820151608082015261439a60c0830161425f565b60a08201526143ab60e0830161426a565b60c08201526101008201516001600160401b038111156143ca57600080fd5b6143d68682850161427b565b60e0830152506143e9610120830161425f565b6101008201526143fc610140830161425f565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561443357600080fd5b61443f8682850161427b565b610180830152506144536101c0830161425f565b6101a08201526144666101e08301614300565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b82811015613f475781546001600160a01b031686526020909501946001918201910161449b565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6080815260006144fe6080830189614482565b828103602084015261451181888a6144c2565b9050856040840152828103606084015261452c8185876144c2565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016145615761456161410e565b5060010190565b60006020828403121561457a57600080fd5b5051919050565b803563ffffffff8116811461234457600080fd5b6000602082840312156145a757600080fd5b612f8482614581565b808201808211156108285761082861410e565b84815260a08101602082018560005b60028110156145ff5763ffffffff6145e983614581565b16835260209283019291909101906001016145d2565b50505060608201939093526080015292915050565b60006020828403121561462657600080fd5b612f8482614300565b604081526000613f966040830185614482565b60ff81811683821601908111156108285761082861410e565b64ffffffffff81811683821601908111156108285761082861410e565b60408101818360005b60028110156146a0578151835260209283019290910190600101614681565b50505092915050565b64ffffffffff82811682821603908111156108285761082861410e565b80820281158282048414176108285761082861410e565b87815286602082015260c0604082015260006146fc60c0830188613f0c565b86606084015285608084015282810360a084015261471b8185876144c2565b9a9950505050505050505050565b818103818111156108285761082861410e565b60008261475957634e487b7160e01b600052601260045260246000fd5b500490565b6001815b60018411156116745780850481111561477d5761477d61410e565b600184161561478b57908102905b60019390931c928002614762565b6000826147a857506001610828565b816147b557506000610828565b81600181146147cb57600281146147d557614807565b6001915050610828565b60ff8411156147e6576147e661410e565b6001841b915064ffffffffff8211156148015761480161410e565b50610828565b5060208310610133831016604e8410600b841016171561483f575081810a64ffffffffff81111561483a5761483a61410e565b610828565b61484f64ffffffffff848461475e565b8064ffffffffff048211156148665761486661410e565b029392505050565b6000612f8464ffffffffff841664ffffffffff8416614799565b64ffffffffff81811683821602908116908181146148a8576148a861410e565b5092915050565b60018060a01b038816815286602082015285604082015260a0606082015260006148dd60a0830186886144c2565b828103608084015261471b8185876144c2565b60006001600160401b0382111561490957614909614172565b5060051b60200190565b600082601f83011261492457600080fd5b8151614937614932826148f0565b6141b1565b8082825260208201915060208360051b86010192508583111561495957600080fd5b602085015b8381101561497657805183526020928301920161495e565b5095945050505050565b60008060006060848603121561499557600080fd5b83516001600160401b038111156149ab57600080fd5b8401601f810186136149bc57600080fd5b80516149ca614932826148f0565b8082825260208201915060208360051b8501019250888311156149ec57600080fd5b6020840193505b82841015614a0e5783518252602093840193909101906149f3565b6020880151909650925050506001600160401b03811115614a2e57600080fd5b614a3a86828701614913565b604086015190935090506001600160401b03811115614a5857600080fd5b614a6486828701614913565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1bdd568bd039595fd4624b89bd697f998c689807d04c17303be9d1417adbdc056f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102b45760003560e01c806301ffc9a7146102b9578063096b810a146102e1578063099a161a146102f65780630b45f8e9146103175780630b88a79a1461032a5780630bbfade7146103335780630f3e34121461034657806317d61120146103595780631e08d0e81461037a5780632800d82914610382578063291a691b146103955780632e7b716d146103a8578063323beaa5146103bb5780634d6861a6146103ce57806350e6d94c146103e157806350e6fad3146104045780635d5047761461040e5780635efb633c1461042157806362cc89a81461042a5780636c120a951461044a57806370e36bbe1461045d578063715018a61461047057806379ba5097146104785780637c92f5241461048057806385814243146104ad5780638a78bb15146104c05780638cb89ecb146104d35780638d1ddfb1146104f35780638da5cb5b146105095780638e5ce3ad146105115780639015d371146105245780639a7a2ffc146105375780639f0f874a14610574578063a01649301461057d578063a8a4d69b1461059d578063acc52494146105b0578063b2d5d1ac146105c3578063b8ab4704146105cc578063bbe4b803146105ee578063bff232c1146105f8578063c2b40ae41461060b578063c3a0ec301461062b578063c8fe182d1461063c578063ca2869a014610644578063cd6dc68714610664578063cf90b6ed14610677578063da881e5a14610681578063dbb06c9314610694578063e30c3978146106a7578063e4be6e3d146106af578063e4d185db146106c2578063e59e4695146106d5578063e6745e13146106e8578063e82f3b70146106fb578063ebf0c7171461070e578063f165053614610716578063f2fde38b14610730578063f379b0df14610743578063f52fd8031461077d578063f6fc05d5146107ee575b600080fd5b6102cc6102c7366004613d99565b6107f7565b60405190151581526020015b60405180910390f35b6102f46102ef366004613dd8565b61082e565b005b610309610304366004613df5565b61096d565b6040519081526020016102d8565b6102f4610325366004613dd8565b6109a7565b61030961070881565b6102f4610341366004613e56565b610a8a565b6102f4610354366004613df5565b610d4b565b61036c610367366004613df5565b610dc6565b6040516102d8929190613f83565b610309600181565b610309610390366004613df5565b610f76565b6102cc6103a3366004613fb1565b610fc3565b6102cc6103b6366004613dd8565b6111a4565b6102f46103c9366004613dd8565b611255565b6102cc6103dc366004613df5565b61134f565b6102cc6103ef366004613dd8565b60066020526000908152604090205460ff1681565b6103096202a30081565b6102cc61041c366004613fee565b611390565b610309600f5481565b600d5461043d906001600160a01b031681565b6040516102d8919061401e565b6102f4610458366004613df5565b6113d5565b6102f461046b366004613dd8565b611400565b6102f4611477565b6102f461149b565b61049361048e366004614032565b6114da565b6040805192835263ffffffff9091166020830152016102d8565b60015461043d906001600160a01b031681565b6102f46104ce366004613dd8565b61167c565b6103096104e1366004613df5565b60096020526000908152604090205481565b600454600160281b900464ffffffffff16610309565b61043d6117c7565b600b5461043d906001600160a01b031681565b6102cc610532366004613dd8565b6117e2565b61055e610545366004613dd8565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff90911681526020016102d8565b61030960035481565b61059061058b366004613df5565b611800565b6040516102d8919061406a565b6102cc6105ab366004613fee565b611899565b6102f46105be366004613dd8565b6118de565b610309600e5481565b6105df6105da366004613df5565b611975565b6040516102d89392919061407d565b6103096210000081565b6102f4610606366004613dd8565b611ac8565b610309610619366004613df5565b60086020526000908152604090205481565b6001546001600160a01b031661043d565b6102f4611b41565b610309610652366004613df5565b60009081526008602052604090205490565b6102f46106723660046140c0565b611bac565b61030962093a8081565b6102cc61068f366004613df5565b611d34565b60005461043d906001600160a01b031681565b61043d612029565b600c5461043d906001600160a01b031681565b61043d6106d03660046140ec565b612034565b6102f46106e3366004613dd8565b6120d5565b6102f46106f63660046140ec565b61214e565b610309610709366004613df5565b612317565b610309612349565b61071e601481565b60405160ff90911681526020016102d8565b6102f461073e366004613dd8565b61235c565b60045461075f9064ffffffffff80821691600160281b90041682565b6040805164ffffffffff9384168152929091166020830152016102d8565b6107bf61078b366004613df5565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b6040516102d8949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61030960025481565b60006001600160e01b03198216635a23ad1360e01b148061082857506001600160e01b031982166301ffc9a760e01b145b92915050565b6108366117c7565b6001600160a01b0316336001600160a01b0316148061085f57506001546001600160a01b031633145b61087c57604051632864c4e160e01b815260040160405180910390fd5b610885816117e2565b81906108ae576040516381e5828960e01b81526004016108a5919061401e565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff16906108dd90600490836123cd565b6001600160a01b0382166000908152600660205260408120805460ff19169055600280549161090b83614124565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d59261096192869291600160281b900464ffffffffff169061413b565b60405180910390a25050565b6000818152600a60205260408120600481015461099d576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b6109af612619565b600c546001600160a01b0316156109d85760405162035f5560e61b815260040160405180910390fd5b6001600160a01b0381166109ff5760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610a6557600d80546001600160a01b031981169091556000600e8190556040516001600160a01b03909216918291600080516020614a6f83398151915291a2505b6040516001600160a01b03821690600080516020614a8f83398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610ab057610ab061415c565b14610ace57604051634f4b461f60e11b815260040160405180910390fd5b600481015415610af15760405163632a22bb60e01b815260040160405180910390fd5b85610b0f57604051636caad1ed60e11b815260040160405180910390fd5b6000610b7682600601805480602002602001604051908101604052809291908181526020018280548015610b6c57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610b4e575b505050505061264d565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610bdf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c079190810190614310565b9050806101c0015115610c9557610c958b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610c8557602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610c67575b50505050508c878d8d8d8d612759565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610cc7908e908c90600401614474565b600060405180830381600087803b158015610ce157600080fd5b505af1158015610cf5573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610d36969594939291906144eb565b60405180910390a25050505050505050505050565b610d53612619565b60018110158015610d67575062093a808111155b8190610d895760405163028237cd60e61b81526004016108a591815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab7906020015b60405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610dfd57610dfd614172565b604051908082528060200260200182016040528015610e26578160200160208202803683370190505b509450806001600160401b03811115610e4157610e41614172565b604051908082528060200260200182016040528015610e6a578160200160208202803683370190505b5093506000805b83811015610f6c576000856006018281548110610e9057610e90614539565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610ed857610ed861415c565b03610f635780888481518110610ef057610ef0614539565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610f4a57610f4a614539565b602090810291909101015282610f5f8161454f565b9350505b50600101610e71565b5050505050915091565b6000818152600a6020526040812081815460ff166003811115610f9b57610f9b61415c565b03610fb957604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b03163314610fef5760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff1660038111156110145761101461415c565b14611032576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa15801561107c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110a09190614568565b9050806110b36040860160208701614595565b63ffffffff1611156110cb6040860160208701614595565b8290916110f9576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016108a5565b5050815460ff19166001908117835582018590554260028301819055600354611121916145b0565b600383015561113560058301856002613ca7565b5061113e612349565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611190928a928a92916145c3565b60405180910390a250600195945050505050565b60006111af826117e2565b6111bb57506000919050565b6001546001600160a01b03166111e4576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d79061121490859060040161401e565b602060405180830381865afa158015611231573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108289190614614565b61125d612619565b600d546001600160a01b0316806112875760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b03828116908216146112c857604051630d77758160e31b81526001600160a01b039283166004820152911660248201526044016108a5565b505060006202a300600e546112dd91906145b0565b9050804281811015611304576040516337c8270b60e01b81526004016108a5929190614474565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e819055604051600080516020614a8f8339815191529190a2505050565b6000818152600a602052604081206001815460ff1660038111156113755761137561415c565b146113835750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156113cd576113cd61415c565b149392505050565b6113dd612619565b600f819055604051818152600080516020614aaf83398151915290602001610dbb565b611408612619565b6001600160a01b03811661142f5760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b61147f612619565b6040516001623f026d60e01b0319815260040160405180910390fd5b33806114a5612029565b6001600160a01b0316146114ce578060405163118cdaa760e01b81526004016108a5919061401e565b6114d78161280f565b50565b600b5460009081906001600160a01b0316331461150a5760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff1660038111156115305761153061415c565b1461154e57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff16600281111561158f5761158f61415c565b1461159f57600b01549150611674565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b82018054916115d483614124565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b849868660405161161c929190614474565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b6116846117c7565b6001600160a01b0316336001600160a01b031614806116ad57506001546001600160a01b031633145b6116ca57604051632864c4e160e01b815260040160405180910390fd5b6116d3816117e2565b6114d757600454600160281b900464ffffffffff1662100000811061170b576040516335b4ac3f60e01b815260040160405180910390fd5b61171f60046001600160a01b038416612836565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916117718361454f565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539261096192869291600160281b900464ffffffffff169061413b565b6000806117d26129b1565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b6000818152600a60205260409020600481015460609190611834576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561188c57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161186e575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff1660028111156118d5576118d561415c565b14159392505050565b6118e6612619565b6001600160a01b03811661190d5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611961906202a300906145b0565b60405190815260200160405180910390a250565b600081815260096020526040902054606090819081906119a8576040516322e679e360e11b815260040160405180910390fd5b600084815260106020908152604080832060118352818420601284529382902081548351818602810186019094528084529194939092918591830182828015611a1057602002820191906000526020600020905b8154815260200190600101908083116119fc575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611a6257602002820191906000526020600020905b815481526020019060010190808311611a4e575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611ab457602002820191906000526020600020905b815481526020019060010190808311611aa0575b505050505090509250925092509193909250565b611ad0612619565b6001600160a01b038116611af75760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611b49612619565b600d546001600160a01b031680611b735760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b03831691600080516020614a6f83398151915291a250565b6000611bb66129d5565b805490915060ff600160401b82041615906001600160401b0316600081158015611bdd5750825b90506000826001600160401b03166001148015611bf95750303b155b905081158015611c07575080155b15611c255760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611c4e57845460ff60401b1916600160401b1785555b6001600160a01b038716611c755760405163d92e233d60e01b815260040160405180910390fd5b611c7e336129fe565b611c8a60046014612a0f565b611c9386610d4b565b610708600f819055604051908152600080516020614aaf8339815191529060200160405180910390a1611cc46117c7565b6001600160a01b0316876001600160a01b031614611ce557611ce58761280f565b8315611d2b57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611d5957611d5961415c565b03611d7757604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611d8f57611d8f61415c565b14611dad57604051631860f69960e31b815260040160405180910390fd5b80600301544211611dd157604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff16111580611ebd578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b158015611e9b57600080fd5b505af1158015611eaf573d6000803e3d6000fd5b506000979650505050505050565b611ec682612a4e565b815460ff191660021782556006820154600b83018190556000816001600160401b03811115611ef757611ef7614172565b604051908082528060200260200182016040528015611f20578160200160208202803683370190505b50905060005b82811015611f9557846009016000866006018381548110611f4957611f49614539565b60009182526020808320909101546001600160a01b031683528201929092526040019020548251839083908110611f8257611f82614539565b6020908102919091010152600101611f26565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b158015611fdc57600080fd5b505af1158015611ff0573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e5856006018360405161119092919061462f565b6000806117d2612b7f565b6000828152600a602052604081206002815460ff16600381111561205a5761205a61415c565b1461207857604051634f4b461f60e11b815260040160405180910390fd5b600681015483908082106120a1576040516326c5c55b60e11b81526004016108a5929190614474565b50508060060183815481106120b8576120b8614539565b6000918252602090912001546001600160a01b0316949350505050565b6120dd612619565b6001600160a01b0381166121045760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156121735761217361415c565b0361219157604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156121a9576121a961415c565b146121c757604051631860f69960e31b815260040160405180910390fd5b80600301544211156121ec57604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff161561221f5760405163257309f160e11b815260040160405180910390fd5b612228336111a4565b6122455760405163149fbcfd60e11b815260040160405180910390fd5b612250338385612ba3565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff191660011790559091506122cc90839083612d7f565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd8584604051612309929190614474565b60405180910390a350505050565b60008181526009602052604090205480612344576040516322e679e360e11b815260040160405180910390fd5b919050565b600061235760046014612f8b565b905090565b612364612619565b600061236e612b7f565b80546001600160a01b0319166001600160a01b03841690811782559091506123946117c7565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614acf83398151915282106123e757600080fd5b825464ffffffffff600160281b9091048116908216811161240757600080fd5b8260005b8186600101600061241c8488612ff1565b64ffffffffff1681526020019081526020016000208190555060008160016124449190614642565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116124795750612611565b6001851660000361254557600061249a8361249588600161465b565b612ff1565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916124fc91600401614678565b602060405180830381865af4158015612519573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253d9190614568565b9350506125fd565b6000612556836124956001896146a9565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916125b891600401614678565b602060405180830381865af41580156125d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f99190614568565b9350505b50647fffffffff600194851c16930161240b565b505050505050565b336126226117c7565b6001600160a01b03161461264b573360405163118cdaa760e01b81526004016108a5919061401e565b565b80516000908161265e8260146146c6565b6001600160401b0381111561267557612675614172565b6040519080825280601f01601f19166020018201604052801561269f576020820181803683370190505b50905060005b828110156127495760008582815181106126c1576126c1614539565b602002602001015160601b905060008260146126dd91906146c6565b905060005b601481101561273b578281601481106126fd576126fd614539565b1a60f81b8561270c83856145b0565b8151811061271c5761271c614539565b60200101906001600160f81b031916908160001a9053506001016126e2565b5050508060010190506126a5565b5080516020909101209392505050565b8261277757604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b81526004016127b497969594939291906146dd565b602060405180830381865afa1580156127d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f59190614614565b506128038a8585858561300f565b50505050505050505050565b6000612819612b7f565b80546001600160a01b0319168155905061283282613150565b5050565b8154600160281b900464ffffffffff16600080516020614acf833981519152821061286057600080fd5b825464ffffffffff9081169082161061287857600080fd5b61288381600161465b565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b818560010160006128ba8487612ff1565b64ffffffffff16815260208101919091526040016000205560018316156129aa5760006128ec826124956001876146a9565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161294e91600401614678565b602060405180830381865af415801561296b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061298f9190614568565b647fffffffff600195861c16949093509190910190506128a9565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610828565b612a066131ac565b6114d7816131d1565b602060ff82161115612a2057600080fd5b612a31600160ff831681901b614729565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612b7a576000612a6b8260016145b0565b90505b82811015612b71576000846006018381548110612a8d57612a8d614539565b60009182526020822001546006870180546001600160a01b0390921693509084908110612abc57612abc614539565b6000918252602090912001546001600160a01b0390811691508216811015612b675780866006018581548110612af457612af4614539565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612b3857612b38614539565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612a6e565b50600101612a56565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612bc45760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612bed576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612c2491614729565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c919190614568565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d0c9190614568565b905060008111612d2f5760405163aeaddff160e01b815260040160405180910390fd5b6000612d3b828461473c565b905060008111612d5e5760405163149fbcfd60e11b815260040160405180910390fd5b80861115611d2b5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612dff57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050612f84565b60008087600901600085600081548110612e1b57612e1b614539565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612ea7576000896009016000878481548110612e6857612e68614539565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612e9e578092508193505b50600101612e45565b50808610612ebc576000945050505050612f84565b600088600a016000868581548110612ed657612ed6614539565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff19166001836002811115612f1457612f1461415c565b021790555086848381548110612f2c57612f2c614539565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff1611612f9c57600080fd5b602060ff83161115612fad57600080fd5b8254600160281b900464ffffffffff1680612fcc60ff8516600261486e565b64ffffffffff161015612fde57600080fd5b612fe9848285613203565b949350505050565b60008161300560ff851663ffffffff614888565b612f84919061465b565b8061302d57604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661305657604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e9061309790309046908d908d908d908d908d906004016148af565b600060405180830381865afa1580156130b4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526130dc9190810190614980565b60008b815260106020908152604090912084519497509295509093506131059290860190613d49565b506000888152601160209081526040909120835161312592850190613d49565b506000888152601260209081526040909120825161314592840190613d49565b505050505050505050565b600061315a6129b1565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6131b46132bb565b61264b57604051631afcd79f60e31b815260040160405180910390fd5b6131d96131ac565b6001600160a01b0381166114ce576000604051631e4fbdf760e01b81526004016108a5919061401e565b6000602060ff8316111561321657600080fd5b8264ffffffffff166000036132355761322e826132d5565b9050612f84565b6000613242836001614642565b60ff166001600160401b0381111561325c5761325c614172565b604051908082528060200260200182016040528015613285578160200160208202803683370190505b5090506132948585858461392a565b808360ff16815181106132a9576132a9614539565b60200260200101519150509392505050565b60006132c56129d5565b54600160401b900460ff16919050565b60008160ff166000036132ea57506000919050565b8160ff1660010361331c57507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361334e57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361338057507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036133b257507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036133e457507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361341657507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361344857507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361347a57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036134ac57507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036134de57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361351057507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361354257507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361357457507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036135a657507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036135d857507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff1660100361360a57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361363c57507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361366e57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff166013036136a057507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036136d257507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff1660150361370457507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361373657507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361376857507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361379a57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036137cc57507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036137fe57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b0361383057507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c0361386257507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d0361389457507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e036138c657507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f036138f857507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036102b457507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff8316111561393b57600080fd5b60008364ffffffffff161161394f57600080fd5b600061395c6001856146a9565b9050600181166000036139b45784600101600061397a600084612ff1565b64ffffffffff16815260200190815260200160002054826000815181106139a3576139a3614539565b6020026020010181815250506139de565b6139be60006132d5565b826000815181106139d1576139d1614539565b6020026020010181815250505b60005b8360ff168160ff1610156126115760018216600003613ada5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110613a3457613a34614539565b60200260200101518152602001613a4a856132d5565b8152506040518263ffffffff1660e01b8152600401613a699190614678565b602060405180830381865af4158015613a86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aaa9190614568565b83613ab6836001614642565b60ff1681518110613ac957613ac9614539565b602002602001018181525050613c94565b6000613ae7826001614642565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613b8c576000876001016000613b40856001613b2f9190614642565b60018864ffffffffff16901c612ff1565b64ffffffffff1681526020019081526020016000205490508085846001613b679190614642565b60ff1681518110613b7a57613b7a614539565b60200260200101818152505050613c92565b6000876001016000613ba58560018861249591906146a9565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613bfd57613bfd614539565b60200260200101518152506040518263ffffffff1660e01b8152600401613c249190614678565b602060405180830381865af4158015613c41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c659190614568565b85613c71856001614642565b60ff1681518110613c8457613c84614539565b602002602001018181525050505b505b647fffffffff600192831c1691016139e1565b600183019183908215613d395791602002820160005b83821115613d0757833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613cbd565b8015613d375782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613d07565b505b50613d45929150613d84565b5090565b828054828255906000526020600020908101928215613d39579160200282015b82811115613d39578251825591602001919060010190613d69565b5b80821115613d455760008155600101613d85565b600060208284031215613dab57600080fd5b81356001600160e01b031981168114612f8457600080fd5b6001600160a01b03811681146114d757600080fd5b600060208284031215613dea57600080fd5b8135612f8481613dc3565b600060208284031215613e0757600080fd5b5035919050565b60008083601f840112613e2057600080fd5b5081356001600160401b03811115613e3757600080fd5b602083019150836020828501011115613e4f57600080fd5b9250929050565b60008060008060008060008060a0898b031215613e7257600080fd5b8835975060208901356001600160401b03811115613e8f57600080fd5b613e9b8b828c01613e0e565b9098509650506040890135945060608901356001600160401b03811115613ec157600080fd5b613ecd8b828c01613e0e565b90955093505060808901356001600160401b03811115613eec57600080fd5b613ef88b828c01613e0e565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b82811015613f475781516001600160a01b0316865260209586019590910190600101613f20565b5093949350505050565b600081518084526020840193506020830160005b82811015613f47578151865260209586019590910190600101613f65565b604081526000613f966040830185613f0c565b8281036020840152613fa88185613f51565b95945050505050565b600080600060808486031215613fc657600080fd5b833592506020840135915060808401851015613fe157600080fd5b6040840190509250925092565b6000806040838503121561400157600080fd5b82359150602083013561401381613dc3565b809150509250929050565b6001600160a01b0391909116815260200190565b60008060006060848603121561404757600080fd5b83359250602084013561405981613dc3565b929592945050506040919091013590565b602081526000612f846020830184613f0c565b6060815260006140906060830186613f51565b82810360208401526140a28186613f51565b905082810360408401526140b68185613f51565b9695505050505050565b600080604083850312156140d357600080fd5b82356140de81613dc3565b946020939093013593505050565b600080604083850312156140ff57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b6000816141335761413361410e565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b03811182821017156141ab576141ab614172565b60405290565b604051601f8201601f191681016001600160401b03811182821017156141d9576141d9614172565b604052919050565b80516004811061234457600080fd5b600082601f83011261420157600080fd5b604080519081016001600160401b038111828210171561422357614223614172565b806040525080604084018581111561423a57600080fd5b845b8181101561425457805183526020928301920161423c565b509195945050505050565b805161234481613dc3565b805160ff8116811461234457600080fd5b600082601f83011261428c57600080fd5b81516001600160401b038111156142a5576142a5614172565b6142b8601f8201601f19166020016141b1565b8181528460208386010111156142cd57600080fd5b60005b828110156142ec576020818601810151838301820152016142d0565b506000918101602001919091529392505050565b8051801515811461234457600080fd5b60006020828403121561432257600080fd5b81516001600160401b0381111561433857600080fd5b8201610200818503121561434b57600080fd5b614353614188565b81518152614363602083016141e1565b60208201526040828101519082015261437f85606084016141f0565b606082015260a0820151608082015261439a60c0830161425f565b60a08201526143ab60e0830161426a565b60c08201526101008201516001600160401b038111156143ca57600080fd5b6143d68682850161427b565b60e0830152506143e9610120830161425f565b6101008201526143fc610140830161425f565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561443357600080fd5b61443f8682850161427b565b610180830152506144536101c0830161425f565b6101a08201526144666101e08301614300565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b82811015613f475781546001600160a01b031686526020909501946001918201910161449b565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6080815260006144fe6080830189614482565b828103602084015261451181888a6144c2565b9050856040840152828103606084015261452c8185876144c2565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b6000600182016145615761456161410e565b5060010190565b60006020828403121561457a57600080fd5b5051919050565b803563ffffffff8116811461234457600080fd5b6000602082840312156145a757600080fd5b612f8482614581565b808201808211156108285761082861410e565b84815260a08101602082018560005b60028110156145ff5763ffffffff6145e983614581565b16835260209283019291909101906001016145d2565b50505060608201939093526080015292915050565b60006020828403121561462657600080fd5b612f8482614300565b604081526000613f966040830185614482565b60ff81811683821601908111156108285761082861410e565b64ffffffffff81811683821601908111156108285761082861410e565b60408101818360005b60028110156146a0578151835260209283019290910190600101614681565b50505092915050565b64ffffffffff82811682821603908111156108285761082861410e565b80820281158282048414176108285761082861410e565b87815286602082015260c0604082015260006146fc60c0830188613f0c565b86606084015285608084015282810360a084015261471b8185876144c2565b9a9950505050505050505050565b818103818111156108285761082861410e565b60008261475957634e487b7160e01b600052601260045260246000fd5b500490565b6001815b60018411156116745780850481111561477d5761477d61410e565b600184161561478b57908102905b60019390931c928002614762565b6000826147a857506001610828565b816147b557506000610828565b81600181146147cb57600281146147d557614807565b6001915050610828565b60ff8411156147e6576147e661410e565b6001841b915064ffffffffff8211156148015761480161410e565b50610828565b5060208310610133831016604e8410600b841016171561483f575081810a64ffffffffff81111561483a5761483a61410e565b610828565b61484f64ffffffffff848461475e565b8064ffffffffff048211156148665761486661410e565b029392505050565b6000612f8464ffffffffff841664ffffffffff8416614799565b64ffffffffff81811683821602908116908181146148a8576148a861410e565b5092915050565b60018060a01b038816815286602082015285604082015260a0606082015260006148dd60a0830186886144c2565b828103608084015261471b8185876144c2565b60006001600160401b0382111561490957614909614172565b5060051b60200190565b600082601f83011261492457600080fd5b8151614937614932826148f0565b6141b1565b8082825260208201915060208360051b86010192508583111561495957600080fd5b602085015b8381101561497657805183526020928301920161495e565b5095945050505050565b60008060006060848603121561499557600080fd5b83516001600160401b038111156149ab57600080fd5b8401601f810186136149bc57600080fd5b80516149ca614932826148f0565b8082825260208201915060208360051b8501019250888311156149ec57600080fd5b6020840193505b82841015614a0e5783518252602093840193909101906149f3565b6020880151909650925050506001600160401b03811115614a2e57600080fd5b614a3a86828701614913565b604086015190935090506001600160401b03811115614a5857600080fd5b614a6486828701614913565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1bdd568bd039595fd4624b89bd697f998c689807d04c17303be9d1417adbdc056f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "bytecode": "0x6080604052348015600f57600080fd5b506016601a565b60ca565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560695760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c75780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b614cd7806100d96000396000f3fe608060405234801561001057600080fd5b50600436106102f65760003560e01c806301ffc9a7146102fb578063096b810a14610323578063099a161a146103385780630b45f8e9146103595780630b88a79a1461036c5780630bbfade7146103755780630f3e34121461038857806317d611201461039b5780631e08d0e8146103bc5780632800d829146103c4578063291a691b146103d75780632e7b716d146103ea578063323beaa5146103fd5780634d6861a61461041057806350e6d94c1461042357806350e6fad3146104465780635d504776146104505780635efb633c1461046357806362cc89a81461046c578063638a9d181461048c57806366d3da80146104955780636c120a951461049d57806370e36bbe146104b0578063715018a6146104c357806379ba5097146104cb5780637c92f524146104d357806385814243146105005780638a78bb15146105135780638cb89ecb146105265780638d1ddfb1146105465780638da5cb5b1461055c5780638e5ce3ad146105645780639015d3711461057757806392c0118f1461058a578063967966cc1461059d5780639a7a2ffc146105b05780639f0f874a146105ed578063a0164930146105f6578063a8a4d69b14610616578063acc5249414610629578063b0101d381461063c578063b2d5d1ac14610645578063b8ab47041461064e578063bbe4b80314610670578063bff232c11461067a578063c2b40ae41461068d578063c3a0ec30146106ad578063c8fe182d146106be578063ca2869a0146106c6578063cd6dc687146106e6578063ce82b84014610446578063cf90b6ed146106f9578063da881e5a14610703578063dbb06c9314610716578063e30c397814610729578063e4be6e3d14610731578063e4d185db14610744578063e59e469514610757578063e6745e131461076a578063e82f3b701461077d578063ebf0c71714610790578063f165053614610798578063f2fde38b146107b2578063f379b0df146107c5578063f52fd803146107ff578063f6fc05d514610870575b600080fd5b61030e610309366004613f75565b610879565b60405190151581526020015b60405180910390f35b610336610331366004613fb4565b6108b0565b005b61034b610346366004613fd1565b6109ef565b60405190815260200161031a565b610336610367366004613fb4565b610a29565b61034b61070881565b610336610383366004614032565b610b0c565b610336610396366004613fd1565b610dcd565b6103ae6103a9366004613fd1565b610e48565b60405161031a92919061415f565b61034b600181565b61034b6103d2366004613fd1565b610ff8565b61030e6103e536600461418d565b611045565b61030e6103f8366004613fb4565b611226565b61033661040b366004613fb4565b6112d7565b61030e61041e366004613fd1565b6113d1565b61030e610431366004613fb4565b60066020526000908152604090205460ff1681565b61034b6202a30081565b61030e61045e3660046141ca565b611412565b61034b600f5481565b600d5461047f906001600160a01b031681565b60405161031a91906141fa565b61034b60115481565b610336611457565b6103366104ab366004613fd1565b6114cb565b6103366104be366004613fb4565b611517565b61033661158e565b6103366115b2565b6104e66104e136600461420e565b6115f1565b6040805192835263ffffffff90911660208301520161031a565b60015461047f906001600160a01b031681565b610336610521366004613fb4565b611793565b61034b610534366004613fd1565b60096020526000908152604090205481565b600454600160281b900464ffffffffff1661034b565b61047f6118de565b600b5461047f906001600160a01b031681565b61030e610585366004613fb4565b6118f9565b610336610598366004613fd1565b611917565b6103366105ab366004613fd1565b6119dc565b6105d76105be366004613fb4565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161031a565b61034b60035481565b610609610604366004613fd1565b611a2f565b60405161031a9190614246565b61030e6106243660046141ca565b611ac8565b610336610637366004613fb4565b611b0d565b61034b60105481565b61034b600e5481565b61066161065c366004613fd1565b611ba4565b60405161031a93929190614259565b61034b6210000081565b610336610688366004613fb4565b611cf7565b61034b61069b366004613fd1565b60086020526000908152604090205481565b6001546001600160a01b031661047f565b610336611d70565b61034b6106d4366004613fd1565b60009081526008602052604090205490565b6103366106f436600461429c565b611ddb565b61034b62093a8081565b61030e610711366004613fd1565b611f63565b60005461047f906001600160a01b031681565b61047f612258565b600c5461047f906001600160a01b031681565b61047f6107523660046142c8565b612263565b610336610765366004613fb4565b612304565b6103366107783660046142c8565b61237d565b61034b61078b366004613fd1565b612546565b61034b612578565b6107a0601481565b60405160ff909116815260200161031a565b6103366107c0366004613fb4565b61258b565b6004546107e19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161031a565b61084161080d366004613fd1565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161031a949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61034b60025481565b60006001600160e01b03198216630aa0697960e01b14806108aa57506001600160e01b031982166301ffc9a760e01b145b92915050565b6108b86118de565b6001600160a01b0316336001600160a01b031614806108e157506001546001600160a01b031633145b6108fe57604051632864c4e160e01b815260040160405180910390fd5b610907816118f9565b8190610930576040516381e5828960e01b815260040161092791906141fa565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff169061095f90600490836125fc565b6001600160a01b0382166000908152600660205260408120805460ff19169055600280549161098d83614300565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5926109e392869291600160281b900464ffffffffff1690614317565b60405180910390a25050565b6000818152600a602052604081206004810154610a1f576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b610a31612848565b600c546001600160a01b031615610a5a5760405162035f5560e61b815260040160405180910390fd5b6001600160a01b038116610a815760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610ae757600d80546001600160a01b031981169091556000600e8190556040516001600160a01b03909216918291600080516020614c4b83398151915291a2505b6040516001600160a01b03821690600080516020614c6b83398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610b3257610b32614338565b14610b5057604051634f4b461f60e11b815260040160405180910390fd5b600481015415610b735760405163632a22bb60e01b815260040160405180910390fd5b85610b9157604051636caad1ed60e11b815260040160405180910390fd5b6000610bf882600601805480602002602001604051908101604052809291908181526020018280548015610bee57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610bd0575b505050505061287c565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610c61573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c8991908101906144ec565b9050806101c0015115610d1757610d178b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610d0757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ce9575b50505050508c878d8d8d8d612935565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610d49908e908c90600401614650565b600060405180830381600087803b158015610d6357600080fd5b505af1158015610d77573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610db8969594939291906146c7565b60405180910390a25050505050505050505050565b610dd5612848565b60018110158015610de9575062093a808111155b8190610e0b5760405163028237cd60e61b815260040161092791815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab7906020015b60405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610e7f57610e7f61434e565b604051908082528060200260200182016040528015610ea8578160200160208202803683370190505b509450806001600160401b03811115610ec357610ec361434e565b604051908082528060200260200182016040528015610eec578160200160208202803683370190505b5093506000805b83811015610fee576000856006018281548110610f1257610f12614715565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610f5a57610f5a614338565b03610fe55780888481518110610f7257610f72614715565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610fcc57610fcc614715565b602090810291909101015282610fe18161472b565b9350505b50600101610ef3565b5050505050915091565b6000818152600a6020526040812081815460ff16600381111561101d5761101d614338565b0361103b57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b031633146110715760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff16600381111561109657611096614338565b146110b4576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa1580156110fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111229190614744565b9050806111356040860160208701614771565b63ffffffff16111561114d6040860160208701614771565b82909161117b576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610927565b5050815460ff191660019081178355820185905542600283018190556003546111a39161478c565b60038301556111b760058301856002613e83565b506111c0612578565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611212928a928a929161479f565b60405180910390a250600195945050505050565b6000611231826118f9565b61123d57506000919050565b6001546001600160a01b0316611266576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906112969085906004016141fa565b602060405180830381865afa1580156112b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108aa91906147f0565b6112df612848565b600d546001600160a01b0316806113095760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461134a57604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610927565b505060006202a300600e5461135f919061478c565b9050804281811015611386576040516337c8270b60e01b8152600401610927929190614650565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e819055604051600080516020614c6b8339815191529190a2505050565b6000818152600a602052604081206001815460ff1660038111156113f7576113f7614338565b146114055750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561144f5761144f614338565b149392505050565b61145f612848565b6011546000819003611484576040516374c28a4f60e11b815260040160405180910390fd5b601080546000918290556011919091556040518181527f1b445bf433fa24e3bc86b4be0682e0f89cb10813cc447a34c3480bfb32700dce9060200160405180910390a15050565b6114d3612848565b806000036114f457604051630823cf3d60e41b815260040160405180910390fd5b600f819055604051818152600080516020614c8b83398151915290602001610e3d565b61151f612848565b6001600160a01b0381166115465760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b611596612848565b6040516001623f026d60e01b0319815260040160405180910390fd5b33806115bc612258565b6001600160a01b0316146115e5578060405163118cdaa760e01b815260040161092791906141fa565b6115ee816129eb565b50565b600b5460009081906001600160a01b031633146116215760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff16600381111561164757611647614338565b1461166557604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff1660028111156116a6576116a6614338565b146116b657600b0154915061178b565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b82018054916116eb83614300565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611733929190614650565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b61179b6118de565b6001600160a01b0316336001600160a01b031614806117c457506001546001600160a01b031633145b6117e157604051632864c4e160e01b815260040160405180910390fd5b6117ea816118f9565b6115ee57600454600160281b900464ffffffffff16621000008110611822576040516335b4ac3f60e01b815260040160405180910390fd5b61183660046001600160a01b038416612a12565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916118888361472b565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53926109e392869291600160281b900464ffffffffff1690614317565b6000806118e9612b8d565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b61191f612848565b6011546000819003611944576040516374c28a4f60e11b815260040160405180910390fd5b601054808380821461196b57604051632031a2b360e21b8152600401610927929190614650565b506000905061197d6202a3008461478c565b90508042818110156119a457604051630cdb45f960e11b8152600401610927929190614650565b5050600f84905560006010819055601155604051848152600080516020614c8b8339815191529060200160405180910390a150505050565b6119e4612848565b60108190554260118190557f0f002333a4ea310bce79580413099a161d9f7738865c31e2b05fd6e6d869bd55908290611a21906202a3009061478c565b604051610e3d929190614650565b6000818152600a60205260409020600481015460609190611a63576040516322e679e360e11b815260040160405180910390fd5b80600601805480602002602001604051908101604052809291908181526020018280548015611abb57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611a9d575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611b0457611b04614338565b14159392505050565b611b15612848565b6001600160a01b038116611b3c5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611b90906202a3009061478c565b60405190815260200160405180910390a250565b60008181526009602052604090205460609081908190611bd7576040516322e679e360e11b815260040160405180910390fd5b600084815260126020908152604080832060138352818420601484529382902081548351818602810186019094528084529194939092918591830182828015611c3f57602002820191906000526020600020905b815481526020019060010190808311611c2b575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611c9157602002820191906000526020600020905b815481526020019060010190808311611c7d575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611ce357602002820191906000526020600020905b815481526020019060010190808311611ccf575b505050505090509250925092509193909250565b611cff612848565b6001600160a01b038116611d265760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611d78612848565b600d546001600160a01b031680611da25760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b03831691600080516020614c4b83398151915291a250565b6000611de5612bb1565b805490915060ff600160401b82041615906001600160401b0316600081158015611e0c5750825b90506000826001600160401b03166001148015611e285750303b155b905081158015611e36575080155b15611e545760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611e7d57845460ff60401b1916600160401b1785555b6001600160a01b038716611ea45760405163d92e233d60e01b815260040160405180910390fd5b611ead33612bda565b611eb960046014612beb565b611ec286610dcd565b610708600f819055604051908152600080516020614c8b8339815191529060200160405180910390a1611ef36118de565b6001600160a01b0316876001600160a01b031614611f1457611f14876129eb565b8315611f5a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611f8857611f88614338565b03611fa657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611fbe57611fbe614338565b14611fdc57604051631860f69960e31b815260040160405180910390fd5b8060030154421161200057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806120ec578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b1580156120ca57600080fd5b505af11580156120de573d6000803e3d6000fd5b506000979650505050505050565b6120f582612c2a565b815460ff191660021782556006820154600b83018190556000816001600160401b038111156121265761212661434e565b60405190808252806020026020018201604052801561214f578160200160208202803683370190505b50905060005b828110156121c45784600901600086600601838154811061217857612178614715565b60009182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106121b1576121b1614715565b6020908102919091010152600101612155565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b15801561220b57600080fd5b505af115801561221f573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e5856006018360405161121292919061480b565b6000806118e9612d5b565b6000828152600a602052604081206002815460ff16600381111561228957612289614338565b146122a757604051634f4b461f60e11b815260040160405180910390fd5b600681015483908082106122d0576040516326c5c55b60e11b8152600401610927929190614650565b50508060060183815481106122e7576122e7614715565b6000918252602090912001546001600160a01b0316949350505050565b61230c612848565b6001600160a01b0381166123335760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156123a2576123a2614338565b036123c057604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156123d8576123d8614338565b146123f657604051631860f69960e31b815260040160405180910390fd5b806003015442111561241b57604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff161561244e5760405163257309f160e11b815260040160405180910390fd5b61245733611226565b6124745760405163149fbcfd60e11b815260040160405180910390fd5b61247f338385612d7f565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff191660011790559091506124fb90839083612f5b565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd8584604051612538929190614650565b60405180910390a350505050565b60008181526009602052604090205480612573576040516322e679e360e11b815260040160405180910390fd5b919050565b600061258660046014613167565b905090565b612593612848565b600061259d612d5b565b80546001600160a01b0319166001600160a01b03841690811782559091506125c36118de565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614cab833981519152821061261657600080fd5b825464ffffffffff600160281b9091048116908216811161263657600080fd5b8260005b8186600101600061264b84886131cd565b64ffffffffff168152602001908152602001600020819055506000816001612673919061481e565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116126a85750612840565b600185166000036127745760006126c9836126c4886001614837565b6131cd565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161272b91600401614854565b602060405180830381865af4158015612748573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061276c9190614744565b93505061282c565b6000612785836126c4600189614885565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916127e791600401614854565b602060405180830381865af4158015612804573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128289190614744565b9350505b50647fffffffff600194851c16930161263a565b505050505050565b336128516118de565b6001600160a01b03161461287a573360405163118cdaa760e01b815260040161092791906141fa565b565b80516000908161288d8260146148a2565b6001600160401b038111156128a4576128a461434e565b6040519080825280601f01601f1916602001820160405280156128ce576020820181803683370190505b50905060005b828110156129255760008582815181106128f0576128f0614715565b602002602001015160601b9050600082601461290c91906148a2565b60609290921b91840160200191909152506001016128d4565b5080516020909101209392505050565b8261295357604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b815260040161299097969594939291906148b9565b602060405180830381865afa1580156129ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d191906147f0565b506129df8a858585856131eb565b50505050505050505050565b60006129f5612d5b565b80546001600160a01b03191681559050612a0e8261332c565b5050565b8154600160281b900464ffffffffff16600080516020614cab8339815191528210612a3c57600080fd5b825464ffffffffff90811690821610612a5457600080fd5b612a5f816001614837565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b81856001016000612a9684876131cd565b64ffffffffff1681526020810191909152604001600020556001831615612b86576000612ac8826126c4600187614885565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612b2a91600401614854565b602060405180830381865af4158015612b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6b9190614744565b647fffffffff600195861c1694909350919091019050612a85565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006108aa565b612be2613388565b6115ee816133ad565b602060ff82161115612bfc57600080fd5b612c0d600160ff831681901b614905565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612d56576000612c4782600161478c565b90505b82811015612d4d576000846006018381548110612c6957612c69614715565b60009182526020822001546006870180546001600160a01b0390921693509084908110612c9857612c98614715565b6000918252602090912001546001600160a01b0390811691508216811015612d435780866006018581548110612cd057612cd0614715565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612d1457612d14614715565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612c4a565b50600101612c32565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612da05760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612dc9576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612e0091614905565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612e49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e6d9190614744565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ec4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ee89190614744565b905060008111612f0b5760405163aeaddff160e01b815260040160405180910390fd5b6000612f178284614918565b905060008111612f3a5760405163149fbcfd60e11b815260040160405180910390fd5b80861115611f5a5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612fdb57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050613160565b60008087600901600085600081548110612ff757612ff7614715565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b845481101561308357600089600901600087848154811061304457613044614715565b60009182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561307a578092508193505b50600101613021565b50808610613098576000945050505050613160565b600088600a0160008685815481106130b2576130b2614715565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156130f0576130f0614338565b02179055508684838154811061310857613108614715565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff161161317857600080fd5b602060ff8316111561318957600080fd5b8254600160281b900464ffffffffff16806131a860ff85166002614a4a565b64ffffffffff1610156131ba57600080fd5b6131c58482856133df565b949350505050565b6000816131e160ff851663ffffffff614a64565b6131609190614837565b8061320957604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661323257604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e9061327390309046908d908d908d908d908d90600401614a8b565b600060405180830381865afa158015613290573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132b89190810190614b5c565b60008b815260126020908152604090912084519497509295509093506132e19290860190613f25565b506000888152601360209081526040909120835161330192850190613f25565b506000888152601460209081526040909120825161332192840190613f25565b505050505050505050565b6000613336612b8d565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b613390613497565b61287a57604051631afcd79f60e31b815260040160405180910390fd5b6133b5613388565b6001600160a01b0381166115e5576000604051631e4fbdf760e01b815260040161092791906141fa565b6000602060ff831611156133f257600080fd5b8264ffffffffff166000036134115761340a826134b1565b9050613160565b600061341e83600161481e565b60ff166001600160401b038111156134385761343861434e565b604051908082528060200260200182016040528015613461578160200160208202803683370190505b50905061347085858584613b06565b808360ff168151811061348557613485614715565b60200260200101519150509392505050565b60006134a1612bb1565b54600160401b900460ff16919050565b60008160ff166000036134c657506000919050565b8160ff166001036134f857507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361352a57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361355c57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361358e57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036135c057507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036135f257507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361362457507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361365657507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361368857507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036136ba57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036136ec57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361371e57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361375057507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361378257507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036137b457507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036137e657507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361381857507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361384a57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361387c57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036138ae57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036138e057507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361391257507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361394457507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361397657507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036139a857507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036139da57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03613a0c57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03613a3e57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03613a7057507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03613aa257507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03613ad457507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036102f657507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff83161115613b1757600080fd5b60008364ffffffffff1611613b2b57600080fd5b6000613b38600185614885565b905060018116600003613b9057846001016000613b566000846131cd565b64ffffffffff1681526020019081526020016000205482600081518110613b7f57613b7f614715565b602002602001018181525050613bba565b613b9a60006134b1565b82600081518110613bad57613bad614715565b6020026020010181815250505b60005b8360ff168160ff1610156128405760018216600003613cb65773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110613c1057613c10614715565b60200260200101518152602001613c26856134b1565b8152506040518263ffffffff1660e01b8152600401613c459190614854565b602060405180830381865af4158015613c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c869190614744565b83613c9283600161481e565b60ff1681518110613ca557613ca5614715565b602002602001018181525050613e70565b6000613cc382600161481e565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613d68576000876001016000613d1c856001613d0b919061481e565b60018864ffffffffff16901c6131cd565b64ffffffffff1681526020019081526020016000205490508085846001613d43919061481e565b60ff1681518110613d5657613d56614715565b60200260200101818152505050613e6e565b6000876001016000613d81856001886126c49190614885565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613dd957613dd9614715565b60200260200101518152506040518263ffffffff1660e01b8152600401613e009190614854565b602060405180830381865af4158015613e1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e419190614744565b85613e4d85600161481e565b60ff1681518110613e6057613e60614715565b602002602001018181525050505b505b647fffffffff600192831c169101613bbd565b600183019183908215613f155791602002820160005b83821115613ee357833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613e99565b8015613f135782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613ee3565b505b50613f21929150613f60565b5090565b828054828255906000526020600020908101928215613f15579160200282015b82811115613f15578251825591602001919060010190613f45565b5b80821115613f215760008155600101613f61565b600060208284031215613f8757600080fd5b81356001600160e01b03198116811461316057600080fd5b6001600160a01b03811681146115ee57600080fd5b600060208284031215613fc657600080fd5b813561316081613f9f565b600060208284031215613fe357600080fd5b5035919050565b60008083601f840112613ffc57600080fd5b5081356001600160401b0381111561401357600080fd5b60208301915083602082850101111561402b57600080fd5b9250929050565b60008060008060008060008060a0898b03121561404e57600080fd5b8835975060208901356001600160401b0381111561406b57600080fd5b6140778b828c01613fea565b9098509650506040890135945060608901356001600160401b0381111561409d57600080fd5b6140a98b828c01613fea565b90955093505060808901356001600160401b038111156140c857600080fd5b6140d48b828c01613fea565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b828110156141235781516001600160a01b03168652602095860195909101906001016140fc565b5093949350505050565b600081518084526020840193506020830160005b82811015614123578151865260209586019590910190600101614141565b60408152600061417260408301856140e8565b8281036020840152614184818561412d565b95945050505050565b6000806000608084860312156141a257600080fd5b8335925060208401359150608084018510156141bd57600080fd5b6040840190509250925092565b600080604083850312156141dd57600080fd5b8235915060208301356141ef81613f9f565b809150509250929050565b6001600160a01b0391909116815260200190565b60008060006060848603121561422357600080fd5b83359250602084013561423581613f9f565b929592945050506040919091013590565b60208152600061316060208301846140e8565b60608152600061426c606083018661412d565b828103602084015261427e818661412d565b90508281036040840152614292818561412d565b9695505050505050565b600080604083850312156142af57600080fd5b82356142ba81613f9f565b946020939093013593505050565b600080604083850312156142db57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161430f5761430f6142ea565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b03811182821017156143875761438761434e565b60405290565b604051601f8201601f191681016001600160401b03811182821017156143b5576143b561434e565b604052919050565b80516004811061257357600080fd5b600082601f8301126143dd57600080fd5b604080519081016001600160401b03811182821017156143ff576143ff61434e565b806040525080604084018581111561441657600080fd5b845b81811015614430578051835260209283019201614418565b509195945050505050565b805161257381613f9f565b805160ff8116811461257357600080fd5b600082601f83011261446857600080fd5b81516001600160401b038111156144815761448161434e565b614494601f8201601f191660200161438d565b8181528460208386010111156144a957600080fd5b60005b828110156144c8576020818601810151838301820152016144ac565b506000918101602001919091529392505050565b8051801515811461257357600080fd5b6000602082840312156144fe57600080fd5b81516001600160401b0381111561451457600080fd5b8201610200818503121561452757600080fd5b61452f614364565b8151815261453f602083016143bd565b60208201526040828101519082015261455b85606084016143cc565b606082015260a0820151608082015261457660c0830161443b565b60a082015261458760e08301614446565b60c08201526101008201516001600160401b038111156145a657600080fd5b6145b286828501614457565b60e0830152506145c5610120830161443b565b6101008201526145d8610140830161443b565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561460f57600080fd5b61461b86828501614457565b6101808301525061462f6101c0830161443b565b6101a08201526146426101e083016144dc565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b828110156141235781546001600160a01b0316865260209095019460019182019101614677565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6080815260006146da608083018961465e565b82810360208401526146ed81888a61469e565b9050856040840152828103606084015261470881858761469e565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161473d5761473d6142ea565b5060010190565b60006020828403121561475657600080fd5b5051919050565b803563ffffffff8116811461257357600080fd5b60006020828403121561478357600080fd5b6131608261475d565b808201808211156108aa576108aa6142ea565b84815260a08101602082018560005b60028110156147db5763ffffffff6147c58361475d565b16835260209283019291909101906001016147ae565b50505060608201939093526080015292915050565b60006020828403121561480257600080fd5b613160826144dc565b604081526000614172604083018561465e565b60ff81811683821601908111156108aa576108aa6142ea565b64ffffffffff81811683821601908111156108aa576108aa6142ea565b60408101818360005b600281101561487c57815183526020928301929091019060010161485d565b50505092915050565b64ffffffffff82811682821603908111156108aa576108aa6142ea565b80820281158282048414176108aa576108aa6142ea565b87815286602082015260c0604082015260006148d860c08301886140e8565b86606084015285608084015282810360a08401526148f781858761469e565b9a9950505050505050505050565b818103818111156108aa576108aa6142ea565b60008261493557634e487b7160e01b600052601260045260246000fd5b500490565b6001815b600184111561178b57808504811115614959576149596142ea565b600184161561496757908102905b60019390931c92800261493e565b600082614984575060016108aa565b81614991575060006108aa565b81600181146149a757600281146149b1576149e3565b60019150506108aa565b60ff8411156149c2576149c26142ea565b6001841b915064ffffffffff8211156149dd576149dd6142ea565b506108aa565b5060208310610133831016604e8410600b8410161715614a1b575081810a64ffffffffff811115614a1657614a166142ea565b6108aa565b614a2b64ffffffffff848461493a565b8064ffffffffff04821115614a4257614a426142ea565b029392505050565b600061316064ffffffffff841664ffffffffff8416614975565b64ffffffffff8181168382160290811690818114614a8457614a846142ea565b5092915050565b60018060a01b038816815286602082015285604082015260a060608201526000614ab960a08301868861469e565b82810360808401526148f781858761469e565b60006001600160401b03821115614ae557614ae561434e565b5060051b60200190565b600082601f830112614b0057600080fd5b8151614b13614b0e82614acc565b61438d565b8082825260208201915060208360051b860101925085831115614b3557600080fd5b602085015b83811015614b52578051835260209283019201614b3a565b5095945050505050565b600080600060608486031215614b7157600080fd5b83516001600160401b03811115614b8757600080fd5b8401601f81018613614b9857600080fd5b8051614ba6614b0e82614acc565b8082825260208201915060208360051b850101925088831115614bc857600080fd5b6020840193505b82841015614bea578351825260209384019390910190614bcf565b6020880151909650925050506001600160401b03811115614c0a57600080fd5b614c1686828701614aef565b604086015190935090506001600160401b03811115614c3457600080fd5b614c4086828701614aef565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1bdd568bd039595fd4624b89bd697f998c689807d04c17303be9d1417adbdc056f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102f65760003560e01c806301ffc9a7146102fb578063096b810a14610323578063099a161a146103385780630b45f8e9146103595780630b88a79a1461036c5780630bbfade7146103755780630f3e34121461038857806317d611201461039b5780631e08d0e8146103bc5780632800d829146103c4578063291a691b146103d75780632e7b716d146103ea578063323beaa5146103fd5780634d6861a61461041057806350e6d94c1461042357806350e6fad3146104465780635d504776146104505780635efb633c1461046357806362cc89a81461046c578063638a9d181461048c57806366d3da80146104955780636c120a951461049d57806370e36bbe146104b0578063715018a6146104c357806379ba5097146104cb5780637c92f524146104d357806385814243146105005780638a78bb15146105135780638cb89ecb146105265780638d1ddfb1146105465780638da5cb5b1461055c5780638e5ce3ad146105645780639015d3711461057757806392c0118f1461058a578063967966cc1461059d5780639a7a2ffc146105b05780639f0f874a146105ed578063a0164930146105f6578063a8a4d69b14610616578063acc5249414610629578063b0101d381461063c578063b2d5d1ac14610645578063b8ab47041461064e578063bbe4b80314610670578063bff232c11461067a578063c2b40ae41461068d578063c3a0ec30146106ad578063c8fe182d146106be578063ca2869a0146106c6578063cd6dc687146106e6578063ce82b84014610446578063cf90b6ed146106f9578063da881e5a14610703578063dbb06c9314610716578063e30c397814610729578063e4be6e3d14610731578063e4d185db14610744578063e59e469514610757578063e6745e131461076a578063e82f3b701461077d578063ebf0c71714610790578063f165053614610798578063f2fde38b146107b2578063f379b0df146107c5578063f52fd803146107ff578063f6fc05d514610870575b600080fd5b61030e610309366004613f75565b610879565b60405190151581526020015b60405180910390f35b610336610331366004613fb4565b6108b0565b005b61034b610346366004613fd1565b6109ef565b60405190815260200161031a565b610336610367366004613fb4565b610a29565b61034b61070881565b610336610383366004614032565b610b0c565b610336610396366004613fd1565b610dcd565b6103ae6103a9366004613fd1565b610e48565b60405161031a92919061415f565b61034b600181565b61034b6103d2366004613fd1565b610ff8565b61030e6103e536600461418d565b611045565b61030e6103f8366004613fb4565b611226565b61033661040b366004613fb4565b6112d7565b61030e61041e366004613fd1565b6113d1565b61030e610431366004613fb4565b60066020526000908152604090205460ff1681565b61034b6202a30081565b61030e61045e3660046141ca565b611412565b61034b600f5481565b600d5461047f906001600160a01b031681565b60405161031a91906141fa565b61034b60115481565b610336611457565b6103366104ab366004613fd1565b6114cb565b6103366104be366004613fb4565b611517565b61033661158e565b6103366115b2565b6104e66104e136600461420e565b6115f1565b6040805192835263ffffffff90911660208301520161031a565b60015461047f906001600160a01b031681565b610336610521366004613fb4565b611793565b61034b610534366004613fd1565b60096020526000908152604090205481565b600454600160281b900464ffffffffff1661034b565b61047f6118de565b600b5461047f906001600160a01b031681565b61030e610585366004613fb4565b6118f9565b610336610598366004613fd1565b611917565b6103366105ab366004613fd1565b6119dc565b6105d76105be366004613fb4565b60076020526000908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161031a565b61034b60035481565b610609610604366004613fd1565b611a2f565b60405161031a9190614246565b61030e6106243660046141ca565b611ac8565b610336610637366004613fb4565b611b0d565b61034b60105481565b61034b600e5481565b61066161065c366004613fd1565b611ba4565b60405161031a93929190614259565b61034b6210000081565b610336610688366004613fb4565b611cf7565b61034b61069b366004613fd1565b60086020526000908152604090205481565b6001546001600160a01b031661047f565b610336611d70565b61034b6106d4366004613fd1565b60009081526008602052604090205490565b6103366106f436600461429c565b611ddb565b61034b62093a8081565b61030e610711366004613fd1565b611f63565b60005461047f906001600160a01b031681565b61047f612258565b600c5461047f906001600160a01b031681565b61047f6107523660046142c8565b612263565b610336610765366004613fb4565b612304565b6103366107783660046142c8565b61237d565b61034b61078b366004613fd1565b612546565b61034b612578565b6107a0601481565b60405160ff909116815260200161031a565b6103366107c0366004613fb4565b61258b565b6004546107e19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161031a565b61084161080d366004613fd1565b6000908152600a60205260409020600b810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161031a949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b61034b60025481565b60006001600160e01b03198216630aa0697960e01b14806108aa57506001600160e01b031982166301ffc9a760e01b145b92915050565b6108b86118de565b6001600160a01b0316336001600160a01b031614806108e157506001546001600160a01b031633145b6108fe57604051632864c4e160e01b815260040160405180910390fd5b610907816118f9565b8190610930576040516381e5828960e01b815260040161092791906141fa565b60405180910390fd5b506001600160a01b03811660009081526007602052604081205464ffffffffff169061095f90600490836125fc565b6001600160a01b0382166000908152600660205260408120805460ff19169055600280549161098d83614300565b90915550506002546004546040516001600160a01b038516927f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5926109e392869291600160281b900464ffffffffff1690614317565b60405180910390a25050565b6000818152600a602052604081206004810154610a1f576040516322e679e360e11b815260040160405180910390fd5b6007015492915050565b610a31612848565b600c546001600160a01b031615610a5a5760405162035f5560e61b815260040160405180910390fd5b6001600160a01b038116610a815760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383811691909117909155600d541615610ae757600d80546001600160a01b031981169091556000600e8190556040516001600160a01b03909216918291600080516020614c4b83398151915291a2505b6040516001600160a01b03821690600080516020614c6b83398151915290600090a250565b6000888152600a602052604090206002815460ff166003811115610b3257610b32614338565b14610b5057604051634f4b461f60e11b815260040160405180910390fd5b600481015415610b735760405163632a22bb60e01b815260040160405180910390fd5b85610b9157604051636caad1ed60e11b815260040160405180910390fd5b6000610bf882600601805480602002602001604051908101604052809291908181526020018280548015610bee57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610bd0575b505050505061287c565b60078301819055600480840189905560008c8152600960205260408082208b90558154905163101bb4d760e21b81529283018e9052929350916001600160a01b03169063406ed35c90602401600060405180830381865afa158015610c61573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c8991908101906144ec565b9050806101c0015115610d1757610d178b82600860008f81526020019081526020016000205486600601805480602002602001604051908101604052809291908181526020018280548015610d0757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610ce9575b50505050508c878d8d8d8d612935565b6000546040516340a3b76160e11b81526001600160a01b03909116906381476ec290610d49908e908c90600401614650565b600060405180830381600087803b158015610d6357600080fd5b505af1158015610d77573d6000803e3d6000fd5b505050508a7fbf0636a312095f6c09c909823813b50e392323588d2d83432e7512c64041e67f846006018c8c8c8c8c604051610db8969594939291906146c7565b60405180910390a25050505050505050505050565b610dd5612848565b60018110158015610de9575062093a808111155b8190610e0b5760405163028237cd60e61b815260040161092791815260200190565b5060038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab7906020015b60405180910390a150565b6000818152600a602052604090206006810154600b82015460609283929091806001600160401b03811115610e7f57610e7f61434e565b604051908082528060200260200182016040528015610ea8578160200160208202803683370190505b509450806001600160401b03811115610ec357610ec361434e565b604051908082528060200260200182016040528015610eec578160200160208202803683370190505b5093506000805b83811015610fee576000856006018281548110610f1257610f12614715565b6000918252602090912001546001600160a01b0316905060016001600160a01b0382166000908152600a8801602052604090205460ff166002811115610f5a57610f5a614338565b03610fe55780888481518110610f7257610f72614715565b60200260200101906001600160a01b031690816001600160a01b031681525050856009016000826001600160a01b03166001600160a01b0316815260200190815260200160002054878481518110610fcc57610fcc614715565b602090810291909101015282610fe18161472b565b9350505b50600101610ef3565b5050505050915091565b6000818152600a6020526040812081815460ff16600381111561101d5761101d614338565b0361103b57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b600080546001600160a01b031633146110715760405163e4c2a7eb60e01b815260040160405180910390fd5b6000848152600a6020526040812090815460ff16600381111561109657611096614338565b146110b4576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290516000926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa1580156110fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111229190614744565b9050806111356040860160208701614771565b63ffffffff16111561114d6040860160208701614771565b82909161117b576040516344ec930f60e01b815263ffffffff90921660048301526024820152604401610927565b5050815460ff191660019081178355820185905542600283018190556003546111a39161478c565b60038301556111b760058301856002613e83565b506111c0612578565b600087815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092611212928a928a929161479f565b60405180910390a250600195945050505050565b6000611231826118f9565b61123d57506000919050565b6001546001600160a01b0316611266576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d7906112969085906004016141fa565b602060405180830381865afa1580156112b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108aa91906147f0565b6112df612848565b600d546001600160a01b0316806113095760405163366ca30560e11b815260040160405180910390fd5b80826001600160a01b038281169082161461134a57604051630d77758160e31b81526001600160a01b03928316600482015291166024820152604401610927565b505060006202a300600e5461135f919061478c565b9050804281811015611386576040516337c8270b60e01b8152600401610927929190614650565b5050600c80546001600160a01b0385166001600160a01b03199182168117909255600d805490911690556000600e819055604051600080516020614c6b8339815191529190a2505050565b6000818152600a602052604081206001815460ff1660038111156113f7576113f7614338565b146114055750600092915050565b6003015442111592915050565b600060016000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff16600281111561144f5761144f614338565b149392505050565b61145f612848565b6011546000819003611484576040516374c28a4f60e11b815260040160405180910390fd5b601080546000918290556011919091556040518181527f1b445bf433fa24e3bc86b4be0682e0f89cb10813cc447a34c3480bfb32700dce9060200160405180910390a15050565b6114d3612848565b806000036114f457604051630823cf3d60e41b815260040160405180910390fd5b600f819055604051818152600080516020614c8b83398151915290602001610e3d565b61151f612848565b6001600160a01b0381166115465760405163d92e233d60e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b611596612848565b6040516001623f026d60e01b0319815260040160405180910390fd5b33806115bc612258565b6001600160a01b0316146115e5578060405163118cdaa760e01b815260040161092791906141fa565b6115ee816129eb565b50565b600b5460009081906001600160a01b031633146116215760405163fcef374960e01b815260040160405180910390fd5b6000858152600a602052604090206002815460ff16600381111561164757611647614338565b1461166557604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386166000908152600a8301602052604090205463ffffffff909116925060019060ff1660028111156116a6576116a6614338565b146116b657600b0154915061178b565b6001600160a01b0385166000908152600a820160205260408120805460ff19166002179055600b82018054916116eb83614300565b919050555080600b01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051611733929190614650565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b61179b6118de565b6001600160a01b0316336001600160a01b031614806117c457506001546001600160a01b031633145b6117e157604051632864c4e160e01b815260040160405180910390fd5b6117ea816118f9565b6115ee57600454600160281b900464ffffffffff16621000008110611822576040516335b4ac3f60e01b815260040160405180910390fd5b61183660046001600160a01b038416612a12565b6001600160a01b0382166000908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff1990911617905560028054916118888361472b565b90915550506002546004546040516001600160a01b038516927f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db53926109e392869291600160281b900464ffffffffff1690614317565b6000806118e9612b8d565b546001600160a01b031692915050565b6001600160a01b031660009081526006602052604090205460ff1690565b61191f612848565b6011546000819003611944576040516374c28a4f60e11b815260040160405180910390fd5b601054808380821461196b57604051632031a2b360e21b8152600401610927929190614650565b506000905061197d6202a3008461478c565b90508042818110156119a457604051630cdb45f960e11b8152600401610927929190614650565b5050600f84905560006010819055601155604051848152600080516020614c8b8339815191529060200160405180910390a150505050565b6119e4612848565b60108190554260118190557f0f002333a4ea310bce79580413099a161d9f7738865c31e2b05fd6e6d869bd55908290611a21906202a3009061478c565b604051610e3d929190614650565b6000818152600a60205260409020600481015460609190611a63576040516322e679e360e11b815260040160405180910390fd5b80600601805480602002602001604051908101604052809291908181526020018280548015611abb57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611a9d575b5050505050915050919050565b6000806000848152600a602081815260408084206001600160a01b0388168552909201905290205460ff166002811115611b0457611b04614338565b14159392505050565b611b15612848565b6001600160a01b038116611b3c5760405163d92e233d60e01b815260040160405180910390fd5b600d80546001600160a01b0319166001600160a01b03831690811790915542600e8190557fc4d0d0b50812c7ced0867d68b80682e9c1f549fe4f7b398a3a10054d3d9f0f2590611b90906202a3009061478c565b60405190815260200160405180910390a250565b60008181526009602052604090205460609081908190611bd7576040516322e679e360e11b815260040160405180910390fd5b600084815260126020908152604080832060138352818420601484529382902081548351818602810186019094528084529194939092918591830182828015611c3f57602002820191906000526020600020905b815481526020019060010190808311611c2b575b5050505050925081805480602002602001604051908101604052809291908181526020018280548015611c9157602002820191906000526020600020905b815481526020019060010190808311611c7d575b5050505050915080805480602002602001604051908101604052809291908181526020018280548015611ce357602002820191906000526020600020905b815481526020019060010190808311611ccf575b505050505090509250925092509193909250565b611cff612848565b6001600160a01b038116611d265760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0383169081179091556040517fb73e5a0813d035641a46672d94cff1b110eae2a87ac75a0e31134dfba06cffe290600090a250565b611d78612848565b600d546001600160a01b031680611da25760405163366ca30560e11b815260040160405180910390fd5b600d80546001600160a01b03191690556000600e8190556040516001600160a01b03831691600080516020614c4b83398151915291a250565b6000611de5612bb1565b805490915060ff600160401b82041615906001600160401b0316600081158015611e0c5750825b90506000826001600160401b03166001148015611e285750303b155b905081158015611e36575080155b15611e545760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b03191660011785558315611e7d57845460ff60401b1916600160401b1785555b6001600160a01b038716611ea45760405163d92e233d60e01b815260040160405180910390fd5b611ead33612bda565b611eb960046014612beb565b611ec286610dcd565b610708600f819055604051908152600080516020614c8b8339815191529060200160405180910390a1611ef36118de565b6001600160a01b0316876001600160a01b031614611f1457611f14876129eb565b8315611f5a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b6000818152600a6020526040812081815460ff166003811115611f8857611f88614338565b03611fa657604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff166003811115611fbe57611fbe614338565b14611fdc57604051631860f69960e31b815260040160405180910390fd5b8060030154421161200057604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806120ec578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a2600054604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b8290604401600060405180830381600087803b1580156120ca57600080fd5b505af11580156120de573d6000803e3d6000fd5b506000979650505050505050565b6120f582612c2a565b815460ff191660021782556006820154600b83018190556000816001600160401b038111156121265761212661434e565b60405190808252806020026020018201604052801561214f578160200160208202803683370190505b50905060005b828110156121c45784600901600086600601838154811061217857612178614715565b60009182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106121b1576121b1614715565b6020908102919091010152600101612155565b50600054604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d7490602401600060405180830381600087803b15801561220b57600080fd5b505af115801561221f573d6000803e3d6000fd5b50505050857f965338df36bd39d668fe7694af5c34a5e37fb2cdc450ce4e99c0e71deb7c11e5856006018360405161121292919061480b565b6000806118e9612d5b565b6000828152600a602052604081206002815460ff16600381111561228957612289614338565b146122a757604051634f4b461f60e11b815260040160405180910390fd5b600681015483908082106122d0576040516326c5c55b60e11b8152600401610927929190614650565b50508060060183815481106122e7576122e7614715565b6000918252602090912001546001600160a01b0316949350505050565b61230c612848565b6001600160a01b0381166123335760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790600090a250565b6000828152600a6020526040812090815460ff1660038111156123a2576123a2614338565b036123c057604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156123d8576123d8614338565b146123f657604051631860f69960e31b815260040160405180910390fd5b806003015442111561241b57604051639a19114d60e01b815260040160405180910390fd5b33600090815260088201602052604090205460ff161561244e5760405163257309f160e11b815260040160405180910390fd5b61245733611226565b6124745760405163149fbcfd60e11b815260040160405180910390fd5b61247f338385612d7f565b6001810154604080516001600160601b03193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101206000903360008181526008850160205260409020805460ff191660011790559091506124fb90839083612f5b565b50336001600160a01b0316847f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd8584604051612538929190614650565b60405180910390a350505050565b60008181526009602052604090205480612573576040516322e679e360e11b815260040160405180910390fd5b919050565b600061258660046014613167565b905090565b612593612848565b600061259d612d5b565b80546001600160a01b0319166001600160a01b03841690811782559091506125c36118de565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080516020614cab833981519152821061261657600080fd5b825464ffffffffff600160281b9091048116908216811161263657600080fd5b8260005b8186600101600061264b84886131cd565b64ffffffffff168152602001908152602001600020819055506000816001612673919061481e565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff1681116126a85750612840565b600185166000036127745760006126c9836126c4886001614837565b6131cd565b60408051808201825286815264ffffffffff8316600090815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161272b91600401614854565b602060405180830381865af4158015612748573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061276c9190614744565b93505061282c565b6000612785836126c4600189614885565b60408051808201825264ffffffffff8316600090815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe916127e791600401614854565b602060405180830381865af4158015612804573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128289190614744565b9350505b50647fffffffff600194851c16930161263a565b505050505050565b336128516118de565b6001600160a01b03161461287a573360405163118cdaa760e01b815260040161092791906141fa565b565b80516000908161288d8260146148a2565b6001600160401b038111156128a4576128a461434e565b6040519080825280601f01601f1916602001820160405280156128ce576020820181803683370190505b50905060005b828110156129255760008582815181106128f0576128f0614715565b602002602001015160601b9050600082601461290c91906148a2565b60609290921b91840160200191909152506001016128d4565b5080516020909101209392505050565b8261295357604051630fb0193f60e41b815260040160405180910390fd5b8861012001516001600160a01b0316630741a9da8b8a8a8a8a8a8a6040518863ffffffff1660e01b815260040161299097969594939291906148b9565b602060405180830381865afa1580156129ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d191906147f0565b506129df8a858585856131eb565b50505050505050505050565b60006129f5612d5b565b80546001600160a01b03191681559050612a0e8261332c565b5050565b8154600160281b900464ffffffffff16600080516020614cab8339815191528210612a3c57600080fd5b825464ffffffffff90811690821610612a5457600080fd5b612a5f816001614837565b835464ffffffffff91909116600160281b0264ffffffffff60281b199091161783558160005b81856001016000612a9684876131cd565b64ffffffffff1681526020810191909152604001600020556001831615612b86576000612ac8826126c4600187614885565b60408051808201825264ffffffffff8316600090815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91612b2a91600401614854565b602060405180830381865af4158015612b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6b9190614744565b647fffffffff600195861c1694909350919091019050612a85565b5050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930090565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006108aa565b612be2613388565b6115ee816133ad565b602060ff82161115612bfc57600080fd5b612c0d600160ff831681901b614905565b82546001600160501b03191664ffffffffff919091161790915550565b600681015460005b81811015612d56576000612c4782600161478c565b90505b82811015612d4d576000846006018381548110612c6957612c69614715565b60009182526020822001546006870180546001600160a01b0390921693509084908110612c9857612c98614715565b6000918252602090912001546001600160a01b0390811691508216811015612d435780866006018581548110612cd057612cd0614715565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555081866006018481548110612d1457612d14614715565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b5050600101612c4a565b50600101612c32565b505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0090565b60008211612da05760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612dc9576040516350ca893360e01b815260040160405180910390fd5b6000818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd71918891612e0091614905565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015612e49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e6d9190614744565b90506000600160009054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ec4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ee89190614744565b905060008111612f0b5760405163aeaddff160e01b815260040160405180910390fd5b6000612f178284614918565b905060008111612f3a5760405163149fbcfd60e11b815260040160405180910390fd5b80861115611f5a5760405163aeaddff160e01b815260040160405180910390fd5b6005830154600684018054600092600160201b900463ffffffff1690811115612fdb57508054600180820183556000928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526009870182526040808420869055600a88019092529120805460ff1916821790559050613160565b60008087600901600085600081548110612ff757612ff7614715565b60009182526020808320909101546001600160a01b03168352820192909252604001902054905060015b845481101561308357600089600901600087848154811061304457613044614715565b60009182526020808320909101546001600160a01b0316835282019290925260400190205490508281111561307a578092508193505b50600101613021565b50808610613098576000945050505050613160565b600088600a0160008685815481106130b2576130b2614715565b60009182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156130f0576130f0614338565b02179055508684838154811061310857613108614715565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260098a0182526040808220899055600a8b0190925220805460ff191660019081179091559450505050505b9392505050565b6000808260ff161161317857600080fd5b602060ff8316111561318957600080fd5b8254600160281b900464ffffffffff16806131a860ff85166002614a4a565b64ffffffffff1610156131ba57600080fd5b6131c58482856133df565b949350505050565b6000816131e160ff851663ffffffff614a64565b6131609190614837565b8061320957604051639914ab1b60e01b815260040160405180910390fd5b600c546001600160a01b031661323257604051630d6eeecb60e01b815260040160405180910390fd5b600c54604051635a3b618f60e11b8152600091829182916001600160a01b03169063b476c31e9061327390309046908d908d908d908d908d90600401614a8b565b600060405180830381865afa158015613290573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132b89190810190614b5c565b60008b815260126020908152604090912084519497509295509093506132e19290860190613f25565b506000888152601360209081526040909120835161330192850190613f25565b506000888152601460209081526040909120825161332192840190613f25565b505050505050505050565b6000613336612b8d565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b613390613497565b61287a57604051631afcd79f60e31b815260040160405180910390fd5b6133b5613388565b6001600160a01b0381166115e5576000604051631e4fbdf760e01b815260040161092791906141fa565b6000602060ff831611156133f257600080fd5b8264ffffffffff166000036134115761340a826134b1565b9050613160565b600061341e83600161481e565b60ff166001600160401b038111156134385761343861434e565b604051908082528060200260200182016040528015613461578160200160208202803683370190505b50905061347085858584613b06565b808360ff168151811061348557613485614715565b60200260200101519150509392505050565b60006134a1612bb1565b54600160401b900460ff16919050565b60008160ff166000036134c657506000919050565b8160ff166001036134f857507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361352a57507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361355c57507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361358e57507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff166005036135c057507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036135f257507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361362457507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361365657507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361368857507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a036136ba57507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036136ec57507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361371e57507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d0361375057507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e0361378257507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f036137b457507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff166010036137e657507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff1660110361381857507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff1660120361384a57507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff1660130361387c57507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff166014036138ae57507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff166015036138e057507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff1660160361391257507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff1660170361394457507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff1660180361397657507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff166019036139a857507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a036139da57507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03613a0c57507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03613a3e57507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03613a7057507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03613aa257507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03613ad457507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff166020036102f657507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b602060ff83161115613b1757600080fd5b60008364ffffffffff1611613b2b57600080fd5b6000613b38600185614885565b905060018116600003613b9057846001016000613b566000846131cd565b64ffffffffff1681526020019081526020016000205482600081518110613b7f57613b7f614715565b602002602001018181525050613bba565b613b9a60006134b1565b82600081518110613bad57613bad614715565b6020026020010181815250505b60005b8360ff168160ff1610156128405760018216600003613cb65773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110613c1057613c10614715565b60200260200101518152602001613c26856134b1565b8152506040518263ffffffff1660e01b8152600401613c459190614854565b602060405180830381865af4158015613c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c869190614744565b83613c9283600161481e565b60ff1681518110613ca557613ca5614715565b602002602001018181525050613e70565b6000613cc382600161481e565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff16811115613d68576000876001016000613d1c856001613d0b919061481e565b60018864ffffffffff16901c6131cd565b64ffffffffff1681526020019081526020016000205490508085846001613d43919061481e565b60ff1681518110613d5657613d56614715565b60200260200101818152505050613e6e565b6000876001016000613d81856001886126c49190614885565b64ffffffffff16815260200190815260200160002054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff1681518110613dd957613dd9614715565b60200260200101518152506040518263ffffffff1660e01b8152600401613e009190614854565b602060405180830381865af4158015613e1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e419190614744565b85613e4d85600161481e565b60ff1681518110613e6057613e60614715565b602002602001018181525050505b505b647fffffffff600192831c169101613bbd565b600183019183908215613f155791602002820160005b83821115613ee357833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613e99565b8015613f135782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613ee3565b505b50613f21929150613f60565b5090565b828054828255906000526020600020908101928215613f15579160200282015b82811115613f15578251825591602001919060010190613f45565b5b80821115613f215760008155600101613f61565b600060208284031215613f8757600080fd5b81356001600160e01b03198116811461316057600080fd5b6001600160a01b03811681146115ee57600080fd5b600060208284031215613fc657600080fd5b813561316081613f9f565b600060208284031215613fe357600080fd5b5035919050565b60008083601f840112613ffc57600080fd5b5081356001600160401b0381111561401357600080fd5b60208301915083602082850101111561402b57600080fd5b9250929050565b60008060008060008060008060a0898b03121561404e57600080fd5b8835975060208901356001600160401b0381111561406b57600080fd5b6140778b828c01613fea565b9098509650506040890135945060608901356001600160401b0381111561409d57600080fd5b6140a98b828c01613fea565b90955093505060808901356001600160401b038111156140c857600080fd5b6140d48b828c01613fea565b999c989b5096995094979396929594505050565b600081518084526020840193506020830160005b828110156141235781516001600160a01b03168652602095860195909101906001016140fc565b5093949350505050565b600081518084526020840193506020830160005b82811015614123578151865260209586019590910190600101614141565b60408152600061417260408301856140e8565b8281036020840152614184818561412d565b95945050505050565b6000806000608084860312156141a257600080fd5b8335925060208401359150608084018510156141bd57600080fd5b6040840190509250925092565b600080604083850312156141dd57600080fd5b8235915060208301356141ef81613f9f565b809150509250929050565b6001600160a01b0391909116815260200190565b60008060006060848603121561422357600080fd5b83359250602084013561423581613f9f565b929592945050506040919091013590565b60208152600061316060208301846140e8565b60608152600061426c606083018661412d565b828103602084015261427e818661412d565b90508281036040840152614292818561412d565b9695505050505050565b600080604083850312156142af57600080fd5b82356142ba81613f9f565b946020939093013593505050565b600080604083850312156142db57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b60008161430f5761430f6142ea565b506000190190565b64ffffffffff93841681526020810192909252909116604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6040516101e081016001600160401b03811182821017156143875761438761434e565b60405290565b604051601f8201601f191681016001600160401b03811182821017156143b5576143b561434e565b604052919050565b80516004811061257357600080fd5b600082601f8301126143dd57600080fd5b604080519081016001600160401b03811182821017156143ff576143ff61434e565b806040525080604084018581111561441657600080fd5b845b81811015614430578051835260209283019201614418565b509195945050505050565b805161257381613f9f565b805160ff8116811461257357600080fd5b600082601f83011261446857600080fd5b81516001600160401b038111156144815761448161434e565b614494601f8201601f191660200161438d565b8181528460208386010111156144a957600080fd5b60005b828110156144c8576020818601810151838301820152016144ac565b506000918101602001919091529392505050565b8051801515811461257357600080fd5b6000602082840312156144fe57600080fd5b81516001600160401b0381111561451457600080fd5b8201610200818503121561452757600080fd5b61452f614364565b8151815261453f602083016143bd565b60208201526040828101519082015261455b85606084016143cc565b606082015260a0820151608082015261457660c0830161443b565b60a082015261458760e08301614446565b60c08201526101008201516001600160401b038111156145a657600080fd5b6145b286828501614457565b60e0830152506145c5610120830161443b565b6101008201526145d8610140830161443b565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b0381111561460f57600080fd5b61461b86828501614457565b6101808301525061462f6101c0830161443b565b6101a08201526146426101e083016144dc565b6101c0820152949350505050565b918252602082015260400190565b6000815480845260208401935082600052602060002060005b828110156141235781546001600160a01b0316865260209095019460019182019101614677565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6080815260006146da608083018961465e565b82810360208401526146ed81888a61469e565b9050856040840152828103606084015261470881858761469e565b9998505050505050505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161473d5761473d6142ea565b5060010190565b60006020828403121561475657600080fd5b5051919050565b803563ffffffff8116811461257357600080fd5b60006020828403121561478357600080fd5b6131608261475d565b808201808211156108aa576108aa6142ea565b84815260a08101602082018560005b60028110156147db5763ffffffff6147c58361475d565b16835260209283019291909101906001016147ae565b50505060608201939093526080015292915050565b60006020828403121561480257600080fd5b613160826144dc565b604081526000614172604083018561465e565b60ff81811683821601908111156108aa576108aa6142ea565b64ffffffffff81811683821601908111156108aa576108aa6142ea565b60408101818360005b600281101561487c57815183526020928301929091019060010161485d565b50505092915050565b64ffffffffff82811682821603908111156108aa576108aa6142ea565b80820281158282048414176108aa576108aa6142ea565b87815286602082015260c0604082015260006148d860c08301886140e8565b86606084015285608084015282810360a08401526148f781858761469e565b9a9950505050505050505050565b818103818111156108aa576108aa6142ea565b60008261493557634e487b7160e01b600052601260045260246000fd5b500490565b6001815b600184111561178b57808504811115614959576149596142ea565b600184161561496757908102905b60019390931c92800261493e565b600082614984575060016108aa565b81614991575060006108aa565b81600181146149a757600281146149b1576149e3565b60019150506108aa565b60ff8411156149c2576149c26142ea565b6001841b915064ffffffffff8211156149dd576149dd6142ea565b506108aa565b5060208310610133831016604e8410600b8410161715614a1b575081810a64ffffffffff811115614a1657614a166142ea565b6108aa565b614a2b64ffffffffff848461493a565b8064ffffffffff04821115614a4257614a426142ea565b029392505050565b600061316064ffffffffff841664ffffffffff8416614975565b64ffffffffff8181168382160290811690818114614a8457614a846142ea565b5092915050565b60018060a01b038816815286602082015285604082015260a060608201526000614ab960a08301868861469e565b82810360808401526148f781858761469e565b60006001600160401b03821115614ae557614ae561434e565b5060051b60200190565b600082601f830112614b0057600080fd5b8151614b13614b0e82614acc565b61438d565b8082825260208201915060208360051b860101925085831115614b3557600080fd5b602085015b83811015614b52578051835260209283019201614b3a565b5095945050505050565b600080600060608486031215614b7157600080fd5b83516001600160401b03811115614b8757600080fd5b8401601f81018613614b9857600080fd5b8051614ba6614b0e82614acc565b8082825260208201915060208360051b850101925088831115614bc857600080fd5b6020840193505b82841015614bea578351825260209384019390910190614bcf565b6020880151909650925050506001600160401b03811115614c0a57600080fd5b614c1686828701614aef565b604086015190935090506001600160401b03811115614c3457600080fd5b614c4086828701614aef565b915050925092509256fe70d4f2aeee99318ec9b9950ac8942b1853f1271010f9afec7d5d51d16145eb8c69d0aaccd9fd7c013f79cb6049c6c097cf0c02dd6111daf1f7be30836a119a1bdd568bd039595fd4624b89bd697f998c689807d04c17303be9d1417adbdc056f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 9647 + "start": 10206 }, { "length": 20, - "start": 9835 + "start": 10394 }, { "length": 20, - "start": 10753 + "start": 11229 }, { "length": 20, - "start": 15061 + "start": 15537 }, { "length": 20, - "start": 15512 + "start": 15988 } ] } @@ -1812,28 +1958,28 @@ "PoseidonT3": [ { "length": 20, - "start": 9430 + "start": 9989 }, { "length": 20, - "start": 9618 + "start": 10177 }, { "length": 20, - "start": 10536 + "start": 11012 }, { "length": 20, - "start": 14844 + "start": 15320 }, { "length": 20, - "start": 15295 + "start": 15771 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-4a1ab4524682ea3571c5165bfb08d5f4cb6afec8" + "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index 1e5352dd1..0abbea7b2 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -202,6 +202,20 @@ interface ICiphernodeRegistry { /// @param accusationVoteValidity New validity window, in seconds. event AccusationVoteValiditySet(uint256 accusationVoteValidity); + /// @notice Emitted when the owner proposes a new accusation vote validity value. + /// @param accusationVoteValidity Pending validity window, in seconds. + /// @param readyAt Earliest timestamp when commit is allowed. + event AccusationVoteValidityProposed( + uint256 accusationVoteValidity, + uint256 readyAt + ); + + /// @notice Emitted when the owner cancels a pending accusation vote validity proposal. + /// @param accusationVoteValidity Pending value that was cancelled. + event AccusationVoteValidityProposalCancelled( + uint256 accusationVoteValidity + ); + //////////////////////////////////////////////////////////// // // // Errors // @@ -277,6 +291,19 @@ interface ICiphernodeRegistry { /// not match the pending proposal (prevents commit-time substitution). error VerifierMismatch(address pending, address provided); + /// @notice A vote-validity commit was attempted before timelock elapsed. + error AccusationVoteValidityTimelockActive(uint256 readyAt, uint256 nowAt); + + /// @notice `commitAccusationVoteValidity` was called but no proposal is pending. + error NoPendingAccusationVoteValidityUpdate(); + + /// @notice `commitAccusationVoteValidity` was called with value that does not match pending. + error AccusationVoteValidityMismatch(uint256 pending, uint256 provided); + + /// @notice Directly setting `accusationVoteValidity` to zero is disallowed. + /// Use `proposeAccusationVoteValidity` + `commitAccusationVoteValidity`. + error AccusationVoteValidityZeroRequiresTimelock(); + /// @notice Node has already submitted a ticket for this E3 error NodeAlreadySubmitted(); @@ -464,6 +491,28 @@ interface ICiphernodeRegistry { uint256 _sortitionSubmissionWindow ) external; + /// @notice Returns registry-wide accusation vote validity window (seconds). + function accusationVoteValidity() external view returns (uint256); + + /// @notice Sets nonzero accusation vote validity directly. + /// @dev Setting zero requires timelocked propose/commit flow. + function setAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external; + + /// @notice Propose accusation vote validity (supports zero). + function proposeAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external; + + /// @notice Commit accusation vote validity after timelock. + function commitAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external; + + /// @notice Cancel a pending accusation vote validity proposal. + function cancelAccusationVoteValidityProposal() external; + /// @notice Submit a ticket for sortition /// @dev Validates ticket against node's balance at request block /// @param e3Id ID of the E3 computation diff --git a/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol b/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol index 129ec51f0..d5ff6667c 100644 --- a/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol +++ b/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol @@ -7,6 +7,7 @@ pragma solidity 0.8.28; import { IBondingRegistry } from "./IBondingRegistry.sol"; +import { ICiphernodeRegistry } from "./ICiphernodeRegistry.sol"; import { IE3RefundManager } from "./IE3RefundManager.sol"; /** @@ -134,6 +135,9 @@ interface ISlashingManager { /// @notice Thrown when the operator is not a member of the committee for this E3 error OperatorNotInCommittee(); + /// @notice Thrown when a requested DKG `partyId` is not present in stored anchors for this E3 + error PartyIdNotInDkgAnchors(); + /// @notice Thrown when the verifier address in signed evidence doesn't match the policy's current verifier error VerifierMismatch(); @@ -419,6 +423,15 @@ interface ISlashingManager { view returns (IBondingRegistry registry); + /** + * @notice Returns the ciphernode registry contract used for committee checks and DKG anchors + * @return registry Address of the ciphernode registry + */ + function ciphernodeRegistry() + external + view + returns (ICiphernodeRegistry registry); + // ====================== // Admin Functions // ====================== @@ -503,6 +516,22 @@ interface ISlashingManager { bytes calldata proof ) external returns (uint256 proposalId); + /** + * @notice Creates a new Lane A slash proposal by DKG `partyId` attribution. + * @dev Resolves `operator = topNodes[partyId]` and requires `partyId` to be present in + * `CiphernodeRegistry.getDkgAnchors(e3Id).partyIds` before processing attestation evidence. + * This provides an explicit on-chain chain from DKG fold row/slot attribution to operator. + * @param e3Id ID of the E3 computation this slash relates to + * @param partyId Canonical committee slot / DKG party identifier + * @param proof Attestation evidence: abi.encode(proofType, voters, dataHashes, deadline, signatures) + * @return proposalId Sequential ID of the created proposal + */ + function proposeSlashByDkgParty( + uint256 e3Id, + uint256 partyId, + bytes calldata proof + ) external returns (uint256 proposalId); + /** * @notice Creates a new slash proposal with evidence (Lane B - SLASHER_ROLE required) * @dev Only callable by SLASHER_ROLE. Evidence-based slashes have appeal windows. diff --git a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol index 6a405ce2a..c241fc03f 100644 --- a/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol +++ b/packages/enclave-contracts/contracts/lib/CommitteeHashLib.sol @@ -25,8 +25,12 @@ library CommitteeHashLib { for (uint256 i = 0; i < n; ++i) { bytes20 a = bytes20(nodes[i]); uint256 offset = i * 20; - for (uint256 j = 0; j < 20; ++j) { - packed[offset + j] = a[j]; + // Write 20-byte address in one word store; trailing 12 bytes are + // zeroed and either overwritten by the next address or ignored by + // `keccak256(packed)` because bytes length is exactly `20*n`. + // solhint-disable-next-line no-inline-assembly + assembly { + mstore(add(add(packed, 0x20), offset), shl(96, a)) } } return keccak256(packed); diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index 98f8fcb79..73dc2cff0 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -155,6 +155,14 @@ contract CiphernodeRegistryOwnable is /// congestion while keeping stolen signatures from being replayed indefinitely. uint256 public constant DEFAULT_ACCUSATION_VOTE_VALIDITY = 30 minutes; + /// @notice Minimum delay between proposing and committing a zeroing vote-validity update. + /// @dev Mirrors verifier critical-change timelock posture for slash-disable behavior. + uint256 public constant ACCUSATION_VOTE_VALIDITY_TIMELOCK = 2 days; + + /// @notice Pending vote-validity proposal awaiting commit. `pendingAt == 0` means none. + uint256 public pendingAccusationVoteValidity; + uint256 public pendingAccusationVoteValidityAt; + /// @notice DKG anchor commitments stored when the committee public key is published. mapping(uint256 e3Id => uint256[] partyIds) internal dkgPartyIds; mapping(uint256 e3Id => bytes32[] skAggCommits) internal dkgSkAggCommits; @@ -679,10 +687,60 @@ contract CiphernodeRegistryOwnable is function setAccusationVoteValidity( uint256 _accusationVoteValidity ) external onlyOwner { + require( + _accusationVoteValidity != 0, + AccusationVoteValidityZeroRequiresTimelock() + ); + accusationVoteValidity = _accusationVoteValidity; + emit AccusationVoteValiditySet(_accusationVoteValidity); + } + + /// @notice Propose a new accusation vote validity window (supports zero). + /// @dev Zeroing the window is slash-disable behavior and therefore timelocked. + function proposeAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external onlyOwner { + pendingAccusationVoteValidity = _accusationVoteValidity; + pendingAccusationVoteValidityAt = block.timestamp; + emit AccusationVoteValidityProposed( + _accusationVoteValidity, + block.timestamp + ACCUSATION_VOTE_VALIDITY_TIMELOCK + ); + } + + /// @notice Commit a previously proposed accusation vote validity update. + /// @param _accusationVoteValidity Must match the pending proposal. + function commitAccusationVoteValidity( + uint256 _accusationVoteValidity + ) external onlyOwner { + uint256 pendingAt = pendingAccusationVoteValidityAt; + require(pendingAt != 0, NoPendingAccusationVoteValidityUpdate()); + uint256 pending = pendingAccusationVoteValidity; + require( + pending == _accusationVoteValidity, + AccusationVoteValidityMismatch(pending, _accusationVoteValidity) + ); + uint256 readyAt = pendingAt + ACCUSATION_VOTE_VALIDITY_TIMELOCK; + require( + block.timestamp >= readyAt, + AccusationVoteValidityTimelockActive(readyAt, block.timestamp) + ); accusationVoteValidity = _accusationVoteValidity; + pendingAccusationVoteValidity = 0; + pendingAccusationVoteValidityAt = 0; emit AccusationVoteValiditySet(_accusationVoteValidity); } + /// @notice Cancel a pending accusation vote validity proposal. + function cancelAccusationVoteValidityProposal() external onlyOwner { + uint256 pendingAt = pendingAccusationVoteValidityAt; + require(pendingAt != 0, NoPendingAccusationVoteValidityUpdate()); + uint256 pending = pendingAccusationVoteValidity; + pendingAccusationVoteValidity = 0; + pendingAccusationVoteValidityAt = 0; + emit AccusationVoteValidityProposalCancelled(pending); + } + //////////////////////////////////////////////////////////// // // // Get Functions // diff --git a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol index bfb8508fa..cb502199e 100644 --- a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol +++ b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol @@ -352,6 +352,26 @@ contract SlashingManager is address operator, bytes calldata proof ) external returns (uint256 proposalId) { + return _proposeSlash(e3Id, operator, proof); + } + + /// @inheritdoc ISlashingManager + /// @dev Additive attribution path: resolves `operator` from DKG anchors and + /// canonical committee slot before reusing Lane A attestation validation. + function proposeSlashByDkgParty( + uint256 e3Id, + uint256 partyId, + bytes calldata proof + ) external returns (uint256 proposalId) { + address operator = _resolveDkgPartyOperator(e3Id, partyId); + return _proposeSlash(e3Id, operator, proof); + } + + function _proposeSlash( + uint256 e3Id, + address operator, + bytes calldata proof + ) internal returns (uint256 proposalId) { require(operator != address(0), ZeroAddress()); require(proof.length != 0, ProofRequired()); @@ -417,6 +437,27 @@ contract SlashingManager is } } + /// @dev Resolve a slash target from DKG anchors: + /// - `partyId` must be present in `getDkgAnchors(e3Id).partyIds` + /// - operator is resolved as canonical committee slot `topNodes[partyId]` + function _resolveDkgPartyOperator( + uint256 e3Id, + uint256 partyId + ) internal view returns (address operator) { + (uint256[] memory partyIds, , ) = ciphernodeRegistry.getDkgAnchors( + e3Id + ); + bool found = false; + for (uint256 i = 0; i < partyIds.length; i++) { + if (partyIds[i] == partyId) { + found = true; + break; + } + } + require(found, PartyIdNotInDkgAnchors()); + return ciphernodeRegistry.canonicalCommitteeNodeAt(e3Id, partyId); + } + /// @inheritdoc ISlashingManager /// @dev Lane B: Evidence-based slash with appeal window. SLASHER_ROLE required. function proposeSlashEvidence( diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index 51a0bd6ef..26085dacd 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -15,6 +15,10 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { /// @notice Configurable threshold M per E3 for testing mapping(uint256 e3Id => uint32 threshold) private _thresholdM; + uint256 private _accusationVoteValidity = 30 minutes; + mapping(uint256 e3Id => uint256[] partyIds) private _dkgPartyIds; + mapping(uint256 e3Id => bytes32[] skAggCommits) private _dkgSkAggCommits; + mapping(uint256 e3Id => bytes32[] esmAggCommits) private _dkgEsmAggCommits; /// @notice Set committee members for an E3 (test helper) function setCommitteeNodes( @@ -32,6 +36,27 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { _thresholdM[e3Id] = m; } + /// @notice Set DKG anchors for an E3 (test helper) + function setDkgAnchors( + uint256 e3Id, + uint256[] calldata partyIds, + bytes32[] calldata skAggCommits, + bytes32[] calldata esmAggCommits + ) external { + delete _dkgPartyIds[e3Id]; + delete _dkgSkAggCommits[e3Id]; + delete _dkgEsmAggCommits[e3Id]; + for (uint256 i = 0; i < partyIds.length; i++) { + _dkgPartyIds[e3Id].push(partyIds[i]); + } + for (uint256 i = 0; i < skAggCommits.length; i++) { + _dkgSkAggCommits[e3Id].push(skAggCommits[i]); + } + for (uint256 i = 0; i < esmAggCommits.length; i++) { + _dkgEsmAggCommits[e3Id].push(esmAggCommits[i]); + } + } + function requestCommittee( uint256, uint256, @@ -85,17 +110,21 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { } function getDkgAnchors( - uint256 + uint256 e3Id ) external - pure + view returns ( uint256[] memory partyIds, bytes32[] memory skAggCommits, bytes32[] memory esmAggCommits ) { - return (partyIds, skAggCommits, esmAggCommits); + return ( + _dkgPartyIds[e3Id], + _dkgSkAggCommits[e3Id], + _dkgEsmAggCommits[e3Id] + ); } function root() external pure returns (uint256) { @@ -132,6 +161,23 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { return 0; } + function accusationVoteValidity() external view returns (uint256) { + return _accusationVoteValidity; + } + + function setAccusationVoteValidity(uint256 v) external { + _accusationVoteValidity = v; + } + + // solhint-disable-next-line no-empty-blocks + function proposeAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function commitAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function cancelAccusationVoteValidityProposal() external pure {} + // solhint-disable-next-line no-empty-blocks function setSortitionSubmissionWindow(uint256) external pure {} @@ -296,6 +342,22 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { return 0; } + function accusationVoteValidity() external pure returns (uint256) { + return 30 minutes; + } + + // solhint-disable-next-line no-empty-blocks + function setAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function proposeAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function commitAccusationVoteValidity(uint256) external pure {} + + // solhint-disable-next-line no-empty-blocks + function cancelAccusationVoteValidityProposal() external pure {} + // solhint-disable-next-line no-empty-blocks function setSortitionSubmissionWindow(uint256) external pure {} diff --git a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol index 0f7f4ba30..2cd0a874e 100644 --- a/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol +++ b/packages/enclave-contracts/contracts/verifiers/DkgFoldAttestationVerifier.sol @@ -47,8 +47,14 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { function _loadBundle( bytes calldata proof, bytes calldata dkgAttestationBundle - ) private pure returns (BundleData memory data) { - (, data.publicInputs) = abi.decode(proof, (bytes, bytes32[])); + ) private view returns (BundleData memory data) { + try this.decodeProofPublicInputs(proof) returns ( + bytes32[] memory publicInputs + ) { + data.publicInputs = publicInputs; + } catch { + revert ICiphernodeRegistry.InvalidFoldAttestation(); + } data.h = _honestPartyCount(data.publicInputs); // Defense in depth: require the public-inputs `partyId` slots // (`publicInputs[2..2+h]`) to be strictly ascending. The zk circuit @@ -62,13 +68,15 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { ICiphernodeRegistry.InvalidFoldAttestation() ); } - (data.attestations, data.bindings) = abi.decode( - dkgAttestationBundle, - ( - DkgFoldAttestationLib.Attestation[], - DkgFoldAttestationLib.PartySlotBinding[] - ) - ); + try this.decodeAttestationBundle(dkgAttestationBundle) returns ( + DkgFoldAttestationLib.Attestation[] memory attestations, + DkgFoldAttestationLib.PartySlotBinding[] memory bindings + ) { + data.attestations = attestations; + data.bindings = bindings; + } catch { + revert ICiphernodeRegistry.InvalidFoldAttestation(); + } if ( data.attestations.length != data.bindings.length || data.bindings.length != data.h @@ -77,6 +85,33 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { } } + /// @notice Exposed only for guarded decode in `_loadBundle`. + function decodeProofPublicInputs( + bytes calldata proof + ) external pure returns (bytes32[] memory publicInputs) { + (, publicInputs) = abi.decode(proof, (bytes, bytes32[])); + } + + /// @notice Exposed only for guarded decode in `_loadBundle`. + function decodeAttestationBundle( + bytes calldata dkgAttestationBundle + ) + external + pure + returns ( + DkgFoldAttestationLib.Attestation[] memory attestations, + DkgFoldAttestationLib.PartySlotBinding[] memory bindings + ) + { + (attestations, bindings) = abi.decode( + dkgAttestationBundle, + ( + DkgFoldAttestationLib.Attestation[], + DkgFoldAttestationLib.PartySlotBinding[] + ) + ); + } + function _fillAnchors( address registry, uint256 chainId, @@ -124,6 +159,10 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { data.bindings[i].partyId > data.bindings[i - 1].partyId, ICiphernodeRegistry.InvalidFoldAttestation() ); + require( + data.attestations[i].partyId > data.attestations[i - 1].partyId, + ICiphernodeRegistry.InvalidFoldAttestation() + ); } (uint256 slot, bytes32 sk, bytes32 esm) = _verifyBinding( @@ -131,7 +170,8 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { chainId, e3Id, data, - data.bindings[i] + data.bindings[i], + data.attestations[i] ); partyIdsOut[slot] = data.bindings[i].partyId; @@ -158,7 +198,8 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { uint256 chainId, uint256 e3Id, BundleData memory data, - DkgFoldAttestationLib.PartySlotBinding memory binding + DkgFoldAttestationLib.PartySlotBinding memory binding, + DkgFoldAttestationLib.Attestation memory att ) private view returns (uint256 slot, bytes32 skCommit, bytes32 esmCommit) { // Canonical-slot binding: `binding.node` must equal the canonical // operator at index `partyId` of the finalized committee — i.e. @@ -184,9 +225,9 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { ICiphernodeRegistry.InvalidFoldAttestation() ); - DkgFoldAttestationLib.Attestation memory att = _findAttestation( - data.attestations, - binding.partyId + require( + att.partyId == binding.partyId, + ICiphernodeRegistry.InvalidFoldAttestation() ); address signer = DkgFoldAttestationLib.recoverSigner( @@ -220,18 +261,6 @@ contract DkgFoldAttestationVerifier is IDkgFoldAttestationVerifier { return (slot, att.skAggCommit, att.esmAggCommit); } - function _findAttestation( - DkgFoldAttestationLib.Attestation[] memory attestations, - uint256 partyId - ) private pure returns (DkgFoldAttestationLib.Attestation memory att) { - for (uint256 j = 0; j < attestations.length; j++) { - if (attestations[j].partyId == partyId) { - return attestations[j]; - } - } - revert ICiphernodeRegistry.InvalidFoldAttestation(); - } - function _partySlot( bytes32[] memory publicInputs, uint256 partyIdOffset, diff --git a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts index de60bab58..5395c9c29 100644 --- a/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts +++ b/packages/enclave-contracts/test/BfvVkBindingIntegration.spec.ts @@ -141,7 +141,7 @@ describe("BfvVkBindingIntegration", function () { const zkTranscriptLibAddress = await zkTranscriptLib.getAddress(); const dkgAggFactory = await ethers.getContractFactory( - "DkgAggregatorVerifier", + "contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:DkgAggregatorVerifier", { libraries: { "project/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib": @@ -153,7 +153,7 @@ describe("BfvVkBindingIntegration", function () { await dkgAgg.waitForDeployment(); const decAggFactory = await ethers.getContractFactory( - "DecryptionAggregatorVerifier", + "contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol:DecryptionAggregatorVerifier", { libraries: { "project/contracts/verifiers/bfv/honk/DecryptionAggregatorVerifier.sol:ZKTranscriptLib": @@ -380,12 +380,15 @@ describe("BfvVkBindingIntegration", function () { await zkTranscriptLib.waitForDeployment(); const dkgAgg = await ( - await ethers.getContractFactory("DkgAggregatorVerifier", { - libraries: { - "project/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib": - await zkTranscriptLib.getAddress(), + await ethers.getContractFactory( + "contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:DkgAggregatorVerifier", + { + libraries: { + "project/contracts/verifiers/bfv/honk/DkgAggregatorVerifier.sol:ZKTranscriptLib": + await zkTranscriptLib.getAddress(), + }, }, - }) + ) ).deploy(); await dkgAgg.waitForDeployment(); diff --git a/packages/enclave-contracts/test/Enclave.spec.ts b/packages/enclave-contracts/test/Enclave.spec.ts index 684b7a0d6..c5d43c759 100644 --- a/packages/enclave-contracts/test/Enclave.spec.ts +++ b/packages/enclave-contracts/test/Enclave.spec.ts @@ -4,10 +4,10 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. import { expect } from "chai"; -import type { Signer } from "ethers"; import { ADDRESS_TWO as AddressTwo, + buildMockAggregationPublishArgs, deployEnclaveSystem, ENCRYPTION_SCHEME_ID as encryptionSchemeId, ethers, @@ -40,133 +40,6 @@ describe("Enclave", function () { const data = "0xda7a"; const proof = "0x1337"; - /** Public inputs layout for `DkgFoldAttestationVerifier` with `h` honest parties. */ - const encodeMockDkgProofForAttestation = ( - pkCommitment: string, - partyIds: number[], - skCommits: string[], - esmCommits: string[], - ): string => { - const h = partyIds.length; - const publicInputs: string[] = Array.from( - { length: 6 + 3 * h }, - () => ethers.ZeroHash, - ); - publicInputs[publicInputs.length - 1] = pkCommitment; - for (let i = 0; i < h; i++) { - publicInputs[2 + i] = ethers.zeroPadValue( - ethers.toBeHex(partyIds[i]), - 32, - ); - publicInputs[5 + h + i] = skCommits[i]; - publicInputs[5 + 2 * h + i] = esmCommits[i]; - } - return ethers.AbiCoder.defaultAbiCoder().encode( - ["bytes", "bytes32[]"], - ["0x", publicInputs], - ); - }; - - const signFoldAttestation = async ( - signer: Signer, - chainId: bigint, - verifyingContract: string, - e3Id: number, - partyId: number, - skAggCommit: string, - esmAggCommit: string, - ): Promise => { - const domain = { - name: "EnclaveDkgFoldAttestation", - version: "1", - chainId, - verifyingContract, - }; - const types = { - DkgFoldAttestation: [ - { name: "e3Id", type: "uint256" }, - { name: "partyId", type: "uint256" }, - { name: "skAggCommit", type: "bytes32" }, - { name: "esmAggCommit", type: "bytes32" }, - ], - }; - const value = { e3Id, partyId, skAggCommit, esmAggCommit }; - return signer.signTypedData(domain, types, value); - }; - - /** Proof + attestation bundle for `publishCommittee` when proof aggregation is enabled. */ - const buildMockAggregationPublishArgs = async ( - operators: Signer[], - e3Id: number, - publicKey: string, - verifyingContract: string, - ): Promise<{ proof: string; bundle: string }> => { - const pkCommitment = ethers.keccak256(publicKey); - const h = operators.length; - const partyIds = Array.from({ length: h }, (_, i) => i); - const skCommits = partyIds.map((i) => ethers.id(`sk-${e3Id}-${i}`)); - const esmCommits = partyIds.map((i) => ethers.id(`esm-${e3Id}-${i}`)); - const proof = encodeMockDkgProofForAttestation( - pkCommitment, - partyIds, - skCommits, - esmCommits, - ); - - // `topNodes` is address-ascending on chain; bindings must map - // `partyId -> topNodes[partyId]`, so sort operators by address first. - const operatorWithAddrs = await Promise.all( - operators.map(async (op) => ({ op, addr: await op.getAddress() })), - ); - operatorWithAddrs.sort((a, b) => - a.addr.toLowerCase() < b.addr.toLowerCase() - ? -1 - : a.addr.toLowerCase() > b.addr.toLowerCase() - ? 1 - : 0, - ); - - const { chainId } = await ethers.provider.getNetwork(); - const attestations: { - partyId: number; - skAggCommit: string; - esmAggCommit: string; - signature: string; - }[] = []; - const bindings: { partyId: number; node: string }[] = []; - - for (let i = 0; i < h; i++) { - const operator = operatorWithAddrs[i]!.op; - const node = operatorWithAddrs[i]!.addr; - const partyId = partyIds[i]!; - attestations.push({ - partyId, - skAggCommit: skCommits[i]!, - esmAggCommit: esmCommits[i]!, - signature: await signFoldAttestation( - operator, - chainId, - verifyingContract, - e3Id, - partyId, - skCommits[i]!, - esmCommits[i]!, - ), - }); - bindings.push({ partyId, node }); - } - - const bundle = ethers.AbiCoder.defaultAbiCoder().encode( - [ - "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", - "tuple(uint256 partyId, address node)[]", - ], - [attestations, bindings], - ); - - return { proof, bundle }; - }; - const inputWindowDuration = 300; const setup = async () => { diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryVerifierLifecycle.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryVerifierLifecycle.spec.ts new file mode 100644 index 000000000..7cbbfc1e0 --- /dev/null +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryVerifierLifecycle.spec.ts @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { expect } from "chai"; + +import { deployEnclaveSystem, ethers, networkHelpers } from "../fixtures"; + +const { loadFixture, time } = networkHelpers; + +describe("CiphernodeRegistryOwnable verifier lifecycle", function () { + const setup = async () => { + const sys = await deployEnclaveSystem(); + const verifier1 = await ethers.deployContract("DkgFoldAttestationVerifier"); + const verifier2 = await ethers.deployContract("DkgFoldAttestationVerifier"); + const verifier3 = await ethers.deployContract("DkgFoldAttestationVerifier"); + return { + owner: sys.owner, + notTheOwner: sys.notTheOwner, + registry: sys.ciphernodeRegistry, + verifier1, + verifier2, + verifier3, + }; + }; + + it("supports propose/commit with timelock enforcement", async function () { + const { registry, verifier1, verifier2 } = await loadFixture(setup); + + await registry.setInitialDkgFoldAttestationVerifier( + await verifier1.getAddress(), + ); + + const timelock = await registry.DKG_FOLD_VERIFIER_TIMELOCK(); + + await expect( + registry.proposeDkgFoldAttestationVerifier(await verifier2.getAddress()), + ) + .to.emit(registry, "DkgFoldAttestationVerifierProposed") + .withArgs( + await verifier2.getAddress(), + (readyAt: bigint) => readyAt > 0n, + ); + + await expect( + registry.commitDkgFoldAttestationVerifier(await verifier2.getAddress()), + ).to.be.revertedWithCustomError(registry, "VerifierUpdateTimelockActive"); + + await time.increase(Number(timelock) + 1); + + await expect( + registry.commitDkgFoldAttestationVerifier(await verifier2.getAddress()), + ) + .to.emit(registry, "DkgFoldAttestationVerifierUpdated") + .withArgs(await verifier2.getAddress()); + + expect(await registry.dkgFoldAttestationVerifier()).to.equal( + await verifier2.getAddress(), + ); + }); + + it("rejects commit with verifier mismatch", async function () { + const { registry, verifier1, verifier2, verifier3 } = + await loadFixture(setup); + + await registry.setInitialDkgFoldAttestationVerifier( + await verifier1.getAddress(), + ); + await registry.proposeDkgFoldAttestationVerifier( + await verifier2.getAddress(), + ); + await time.increase( + Number(await registry.DKG_FOLD_VERIFIER_TIMELOCK()) + 1, + ); + + await expect( + registry.commitDkgFoldAttestationVerifier(await verifier3.getAddress()), + ) + .to.be.revertedWithCustomError(registry, "VerifierMismatch") + .withArgs(await verifier2.getAddress(), await verifier3.getAddress()); + }); + + it("cleans up stale pending proposal on setInitial", async function () { + const { registry, verifier1, verifier2 } = await loadFixture(setup); + + await registry.proposeDkgFoldAttestationVerifier( + await verifier2.getAddress(), + ); + + await expect( + registry.setInitialDkgFoldAttestationVerifier( + await verifier1.getAddress(), + ), + ) + .to.emit(registry, "DkgFoldAttestationVerifierProposalCancelled") + .withArgs(await verifier2.getAddress()) + .and.to.emit(registry, "DkgFoldAttestationVerifierUpdated") + .withArgs(await verifier1.getAddress()); + + expect(await registry.pendingDkgFoldAttestationVerifier()).to.equal( + ethers.ZeroAddress, + ); + expect(await registry.pendingDkgFoldAttestationVerifierAt()).to.equal(0); + + await expect( + registry.commitDkgFoldAttestationVerifier(await verifier2.getAddress()), + ).to.be.revertedWithCustomError(registry, "NoPendingVerifierUpdate"); + }); + + it("cancels a pending proposal", async function () { + const { registry, verifier2 } = await loadFixture(setup); + + await registry.proposeDkgFoldAttestationVerifier( + await verifier2.getAddress(), + ); + + await expect(registry.cancelDkgFoldAttestationVerifierProposal()) + .to.emit(registry, "DkgFoldAttestationVerifierProposalCancelled") + .withArgs(await verifier2.getAddress()); + + expect(await registry.pendingDkgFoldAttestationVerifier()).to.equal( + ethers.ZeroAddress, + ); + expect(await registry.pendingDkgFoldAttestationVerifierAt()).to.equal(0); + }); + + it("requires timelock to set accusationVoteValidity to zero", async function () { + const { registry } = await loadFixture(setup); + + await expect( + registry.setAccusationVoteValidity(0), + ).to.be.revertedWithCustomError( + registry, + "AccusationVoteValidityZeroRequiresTimelock", + ); + + await registry.proposeAccusationVoteValidity(0); + await expect( + registry.commitAccusationVoteValidity(0), + ).to.be.revertedWithCustomError( + registry, + "AccusationVoteValidityTimelockActive", + ); + + await time.increase( + Number(await registry.ACCUSATION_VOTE_VALIDITY_TIMELOCK()) + 1, + ); + + await expect(registry.commitAccusationVoteValidity(0)) + .to.emit(registry, "AccusationVoteValiditySet") + .withArgs(0); + expect(await registry.accusationVoteValidity()).to.equal(0); + }); + + it("cancels pending accusationVoteValidity proposal", async function () { + const { registry } = await loadFixture(setup); + + await registry.proposeAccusationVoteValidity(1234); + await expect(registry.cancelAccusationVoteValidityProposal()) + .to.emit(registry, "AccusationVoteValidityProposalCancelled") + .withArgs(1234); + + expect(await registry.pendingAccusationVoteValidity()).to.equal(0); + expect(await registry.pendingAccusationVoteValidityAt()).to.equal(0); + }); +}); diff --git a/packages/enclave-contracts/test/Registry/DkgFoldAttestationVerifier.spec.ts b/packages/enclave-contracts/test/Registry/DkgFoldAttestationVerifier.spec.ts new file mode 100644 index 000000000..7bddfc754 --- /dev/null +++ b/packages/enclave-contracts/test/Registry/DkgFoldAttestationVerifier.spec.ts @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import { expect } from "chai"; + +import { + buildMockDkgAttestationFixtureData, + deployEnclaveSystem, + ethers, + networkHelpers, +} from "../fixtures"; + +const { loadFixture } = networkHelpers; + +describe("DkgFoldAttestationVerifier", function () { + const e3Id = 7; + + const setup = async () => { + const sys = await deployEnclaveSystem({ + useMockCiphernodeRegistry: true, + setupOperators: 0, + wireSlashingManager: false, + }); + const verifier = await ethers.deployContract("DkgFoldAttestationVerifier"); + const signers = await ethers.getSigners(); + const operators = [signers[2], signers[3], signers[4]]; + return { + owner: sys.owner, + mockRegistry: sys.mockCiphernodeRegistry!, + verifier, + operators, + }; + }; + + it("verifies a valid bundle and returns anchors", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof, bundle, skCommits, esmCommits } = + await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + const [partyIds, skAggCommits, esmAggCommits] = await verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + bundle, + ); + + expect(partyIds.map((v: bigint) => Number(v))).to.deep.equal([0, 1, 2]); + expect(skAggCommits).to.deep.equal(skCommits); + expect(esmAggCommits).to.deep.equal(esmCommits); + }); + + it("reverts on out-of-order attestations", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof, attestations, bindings } = + await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + const badAttestations = [...attestations]; + [badAttestations[0], badAttestations[1]] = [ + badAttestations[1], + badAttestations[0], + ]; + const badBundle = ethers.AbiCoder.defaultAbiCoder().encode( + [ + "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", + "tuple(uint256 partyId, address node)[]", + ], + [badAttestations, bindings], + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + badBundle, + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); + + it("reverts on duplicate binding partyId", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof, attestations, bindings } = + await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + const badBindings = [...bindings]; + badBindings[1] = { ...badBindings[1], partyId: badBindings[0].partyId }; + const badBundle = ethers.AbiCoder.defaultAbiCoder().encode( + [ + "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", + "tuple(uint256 partyId, address node)[]", + ], + [attestations, badBindings], + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + badBundle, + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); + + it("reverts when signatures are bound to the wrong verifyingContract", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const wrongVerifyingContract = "0x0000000000000000000000000000000000000002"; + const { ordered, proof, bundle } = await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + wrongVerifyingContract, + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + bundle, + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); + + it("reverts when attestation and binding counts mismatch", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof, attestations, bindings } = + await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + const shortBundle = ethers.AbiCoder.defaultAbiCoder().encode( + [ + "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", + "tuple(uint256 partyId, address node)[]", + ], + [attestations.slice(0, 2), bindings], + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + shortBundle, + ), + ).to.be.revertedWithCustomError( + mockRegistry, + "AttestationBindingCountMismatch", + ); + }); + + it("reverts with typed error on malformed proof encoding", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, bundle } = await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + "0x1234", + bundle, + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); + + it("reverts with typed error on malformed bundle encoding", async function () { + const { owner, mockRegistry, verifier, operators } = + await loadFixture(setup); + const { ordered, proof } = await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.id(`pk-${e3Id}`), + await verifier.getAddress(), + ); + await mockRegistry.connect(owner).setCommitteeNodes( + e3Id, + ordered.map((o) => o.addr), + ); + + await expect( + verifier.verify( + await mockRegistry.getAddress(), + 31337, + e3Id, + proof, + "0xabcd", + ), + ).to.be.revertedWithCustomError(mockRegistry, "InvalidFoldAttestation"); + }); +}); diff --git a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts index 4461d2754..03dd7ca9e 100644 --- a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts +++ b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts @@ -48,11 +48,20 @@ describe("SlashingManager", function () { ); } - async function setupPolicies( - slashingManager: SlashingManager, - _mockVerifier?: MockCircuitVerifier, + function buildProofPolicy( + overrides: Partial<{ + ticketPenalty: bigint; + licensePenalty: bigint; + requiresProof: boolean; + proofVerifier: string; + banNode: boolean; + appealWindow: number; + enabled: boolean; + affectsCommittee: boolean; + failureReason: number; + }> = {}, ) { - const proofPolicy = { + return { ticketPenalty: ethers.parseUnits("50", 6), licensePenalty: ethers.parseEther("100"), requiresProof: true, @@ -62,7 +71,15 @@ describe("SlashingManager", function () { enabled: true, affectsCommittee: false, failureReason: 0, + ...overrides, }; + } + + async function setupPolicies( + slashingManager: SlashingManager, + _mockVerifier?: MockCircuitVerifier, + ) { + const proofPolicy = buildProofPolicy(); const evidencePolicy = { ticketPenalty: ethers.parseUnits("20", 6), @@ -76,17 +93,11 @@ describe("SlashingManager", function () { failureReason: 0, }; - const banPolicy = { + const banPolicy = buildProofPolicy({ ticketPenalty: ethers.parseUnits("100", 6), licensePenalty: ethers.parseEther("500"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, banNode: true, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + }); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); await slashingManager.setSlashPolicy(REASON_INACTIVITY, evidencePolicy); @@ -457,17 +468,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); // Set up committee membership: operator must be a member, voters attest the operator is faulty @@ -515,17 +516,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); // Threshold is 2 but only 1 vote provided @@ -565,17 +556,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); const voter1Addr = await voter1.getAddress(); @@ -671,17 +652,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); // Only voter1 is a committee member, but voter2 also signs @@ -705,6 +676,49 @@ describe("SlashingManager", function () { ).to.be.revertedWithCustomError(slashingManager, "VoterNotInCommittee"); }); + it("should revert if attestation deadline has expired", async function () { + const { + slashingManager, + proposer, + operatorAddress, + voter1, + voter2, + mockCiphernodeRegistry, + } = await loadFixture(setup); + + const proofPolicy = buildProofPolicy(); + await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); + + const e3Id = 0; + const voter1Addr = await voter1.getAddress(); + const voter2Addr = await voter2.getAddress(); + await mockCiphernodeRegistry.setCommitteeNodes(e3Id, [ + operatorAddress, + voter1Addr, + voter2Addr, + ]); + await mockCiphernodeRegistry.setThreshold(e3Id, 2); + + const latest = await ethers.provider.getBlock("latest"); + const expiredDeadline = BigInt((latest?.timestamp ?? 0) - 1); + const proof = await signAndEncodeAttestation( + [voter1, voter2], + e3Id, + operatorAddress, + await slashingManager.getAddress(), + 0, + 31337, + ethers.ZeroHash, + expiredDeadline, + ); + + await expect( + slashingManager + .connect(proposer) + .proposeSlash(e3Id, operatorAddress, proof), + ).to.be.revertedWithCustomError(slashingManager, "SignatureExpired"); + }); + it("should revert if operator is zero address", async function () { const { slashingManager, proposer } = await loadFixture(setup); @@ -736,17 +750,7 @@ describe("SlashingManager", function () { const { slashingManager, proposer, operatorAddress } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); await expect( @@ -766,17 +770,7 @@ describe("SlashingManager", function () { mockCiphernodeRegistry, } = await loadFixture(setup); - const proofPolicy = { - ticketPenalty: ethers.parseUnits("50", 6), - licensePenalty: ethers.parseEther("100"), - requiresProof: true, - proofVerifier: ethers.ZeroAddress, - banNode: false, - appealWindow: 0, - enabled: true, - affectsCommittee: false, - failureReason: 0, - }; + const proofPolicy = buildProofPolicy(); await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); const voter1Addr = await voter1.getAddress(); const voter2Addr = await voter2.getAddress(); @@ -896,6 +890,95 @@ describe("SlashingManager", function () { // banNode=true → auto-executed → node is now banned expect(await slashingManager.isBanned(operatorAddress)).to.be.true; }); + + it("should propose slash via DKG partyId attribution", async function () { + const { + slashingManager, + proposer, + operatorAddress, + voter1, + voter2, + mockCiphernodeRegistry, + } = await loadFixture(setup); + + const proofPolicy = buildProofPolicy(); + await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); + + const e3Id = 0; + const voter1Addr = await voter1.getAddress(); + const voter2Addr = await voter2.getAddress(); + await mockCiphernodeRegistry.setCommitteeNodes(e3Id, [ + operatorAddress, + voter1Addr, + voter2Addr, + ]); + await mockCiphernodeRegistry.setThreshold(e3Id, 2); + await (mockCiphernodeRegistry as any).setDkgAnchors( + e3Id, + [0], + [ethers.id("sk-0")], + [ethers.id("esm-0")], + ); + + const proof = await signAndEncodeAttestation( + [voter1, voter2], + e3Id, + operatorAddress, + await slashingManager.getAddress(), + ); + + await expect( + (slashingManager as any) + .connect(proposer) + .proposeSlashByDkgParty(e3Id, 0, proof), + ).to.emit(slashingManager, "SlashProposed"); + }); + + it("should revert if partyId is not in stored DKG anchors", async function () { + const { + slashingManager, + proposer, + operatorAddress, + voter1, + voter2, + mockCiphernodeRegistry, + } = await loadFixture(setup); + + const proofPolicy = buildProofPolicy(); + await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); + + const e3Id = 0; + const voter1Addr = await voter1.getAddress(); + const voter2Addr = await voter2.getAddress(); + await mockCiphernodeRegistry.setCommitteeNodes(e3Id, [ + operatorAddress, + voter1Addr, + voter2Addr, + ]); + await mockCiphernodeRegistry.setThreshold(e3Id, 2); + await (mockCiphernodeRegistry as any).setDkgAnchors( + e3Id, + [1], + [ethers.id("sk-1")], + [ethers.id("esm-1")], + ); + + const proof = await signAndEncodeAttestation( + [voter1, voter2], + e3Id, + operatorAddress, + await slashingManager.getAddress(), + ); + + await expect( + (slashingManager as any) + .connect(proposer) + .proposeSlashByDkgParty(e3Id, 0, proof), + ).to.be.revertedWithCustomError( + slashingManager, + "PartyIdNotInDkgAnchors", + ); + }); }); describe("proposeSlashEvidence() — Lane B (evidence-based, SLASHER_ROLE)", function () { diff --git a/packages/enclave-contracts/test/fixtures/dkgAttestation.ts b/packages/enclave-contracts/test/fixtures/dkgAttestation.ts new file mode 100644 index 000000000..fafd5a62c --- /dev/null +++ b/packages/enclave-contracts/test/fixtures/dkgAttestation.ts @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. +import type { Signer } from "ethers"; + +import { ethers } from "./connection"; + +export type DkgFoldAttestation = { + partyId: number; + skAggCommit: string; + esmAggCommit: string; + signature: string; +}; + +export type DkgPartySlotBinding = { + partyId: number; + node: string; +}; + +/** Public inputs layout expected by `DkgFoldAttestationVerifier` for honest count `h`. */ +export function encodeMockDkgProofForAttestation( + pkCommitment: string, + partyIds: number[], + skCommits: string[], + esmCommits: string[], +): string { + const h = partyIds.length; + const publicInputs: string[] = Array.from( + { length: 6 + 3 * h }, + () => ethers.ZeroHash, + ); + publicInputs[publicInputs.length - 1] = pkCommitment; + for (let i = 0; i < h; i++) { + publicInputs[2 + i] = ethers.zeroPadValue(ethers.toBeHex(partyIds[i]), 32); + publicInputs[5 + h + i] = skCommits[i]; + publicInputs[5 + 2 * h + i] = esmCommits[i]; + } + return ethers.AbiCoder.defaultAbiCoder().encode( + ["bytes", "bytes32[]"], + ["0x", publicInputs], + ); +} + +/** Sign one EIP-712 fold-attestation tuple. */ +export async function signFoldAttestation( + signer: Signer, + chainId: bigint, + verifyingContract: string, + e3Id: number, + partyId: number, + skAggCommit: string, + esmAggCommit: string, +): Promise { + const domain = { + name: "EnclaveDkgFoldAttestation", + version: "1", + chainId, + verifyingContract, + }; + const types = { + DkgFoldAttestation: [ + { name: "e3Id", type: "uint256" }, + { name: "partyId", type: "uint256" }, + { name: "skAggCommit", type: "bytes32" }, + { name: "esmAggCommit", type: "bytes32" }, + ], + }; + return signer.signTypedData(domain, types, { + e3Id, + partyId, + skAggCommit, + esmAggCommit, + }); +} + +/** + * Build mock proof + bundle payloads for `publishCommittee`/verifier tests. + * Operators are sorted by address to match on-chain canonical `topNodes`. + */ +export async function buildMockDkgAttestationFixtureData( + operators: Signer[], + e3Id: number, + pkCommitment: string, + signingVerifierAddress: string, +): Promise<{ + ordered: { op: Signer; addr: string }[]; + proof: string; + bundle: string; + partyIds: number[]; + skCommits: string[]; + esmCommits: string[]; + attestations: DkgFoldAttestation[]; + bindings: DkgPartySlotBinding[]; +}> { + const ordered = await Promise.all( + operators.map(async (op) => ({ op, addr: await op.getAddress() })), + ); + ordered.sort((a, b) => + a.addr.toLowerCase() < b.addr.toLowerCase() + ? -1 + : a.addr.toLowerCase() > b.addr.toLowerCase() + ? 1 + : 0, + ); + + const partyIds = ordered.map((_, idx) => idx); + const skCommits = partyIds.map((i) => ethers.id(`sk-${e3Id}-${i}`)); + const esmCommits = partyIds.map((i) => ethers.id(`esm-${e3Id}-${i}`)); + const proof = encodeMockDkgProofForAttestation( + pkCommitment, + partyIds, + skCommits, + esmCommits, + ); + + const { chainId } = await ethers.provider.getNetwork(); + const attestations: DkgFoldAttestation[] = []; + const bindings: DkgPartySlotBinding[] = []; + for (let i = 0; i < ordered.length; i++) { + attestations.push({ + partyId: i, + skAggCommit: skCommits[i], + esmAggCommit: esmCommits[i], + signature: await signFoldAttestation( + ordered[i].op, + chainId, + signingVerifierAddress, + e3Id, + i, + skCommits[i], + esmCommits[i], + ), + }); + bindings.push({ partyId: i, node: ordered[i].addr }); + } + + const bundle = ethers.AbiCoder.defaultAbiCoder().encode( + [ + "tuple(uint256 partyId, bytes32 skAggCommit, bytes32 esmAggCommit, bytes signature)[]", + "tuple(uint256 partyId, address node)[]", + ], + [attestations, bindings], + ); + + return { + ordered, + proof, + bundle, + partyIds, + skCommits, + esmCommits, + attestations, + bindings, + }; +} + +/** Convenience helper for Enclave tests with a plaintext public key input. */ +export async function buildMockAggregationPublishArgs( + operators: Signer[], + e3Id: number, + publicKey: string, + signingVerifierAddress: string, +): Promise<{ proof: string; bundle: string }> { + const fixture = await buildMockDkgAttestationFixtureData( + operators, + e3Id, + ethers.keccak256(publicKey), + signingVerifierAddress, + ); + return { proof: fixture.proof, bundle: fixture.bundle }; +} diff --git a/packages/enclave-contracts/test/fixtures/index.ts b/packages/enclave-contracts/test/fixtures/index.ts index 386b65134..a8e988565 100644 --- a/packages/enclave-contracts/test/fixtures/index.ts +++ b/packages/enclave-contracts/test/fixtures/index.ts @@ -6,6 +6,7 @@ export { VOTE_TYPEHASH, signAndEncodeAttestation } from "./attestation"; export { connection, ethers, ignition, networkHelpers } from "./connection"; +export * from "./dkgAttestation"; export * from "./constants"; export * from "./helpers"; export { setupOperatorForSortition } from "./operators"; From 78d9f580ae785aba166ec5a0aef9ff6dbcc8487e Mon Sep 17 00:00:00 2001 From: 0xjei Date: Sun, 24 May 2026 15:30:31 +0200 Subject: [PATCH 54/54] new payload format for slashing evidence --- .../flow-trace/05_FAILURE_REFUND_SLASHING.md | 17 +++- .../src/ciphernode_builder.rs | 21 +++- .../accusation_quorum_reached.rs | 12 +-- crates/evm/src/slashing_manager_sol_writer.rs | 23 +++-- .../src/actors/accusation_manager.rs | 96 +++++++++++++++++++ .../src/actors/accusation_manager_ext.rs | 5 + docs/pages/internals/dkg.mdx | 3 +- examples/CRISP/server/src/deployments.rs | 14 ++- .../contracts/Enclave.sol/Enclave.json | 2 +- .../ISlashingManager.json | 2 +- .../CiphernodeRegistryOwnable.json | 2 +- .../contracts/interfaces/ISlashingManager.sol | 10 +- .../contracts/slashing/SlashingManager.sol | 11 ++- .../test/Slashing/CommitteeExpulsion.spec.ts | 16 ++-- .../test/Slashing/SlashingManager.spec.ts | 59 ++++++++++-- .../test/fixtures/attestation.ts | 17 ++-- 16 files changed, 249 insertions(+), 61 deletions(-) diff --git a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md index d80277dba..e5d9481e8 100644 --- a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md +++ b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md @@ -331,13 +331,20 @@ ProofFailureAccusation arrives via P2P from another committee member │ ├─ 1. Verify accuser is a committee member │ -├─ 2. Verify accuser's ECDSA signature on accusation digest +├─ 2. Validate accusation deadline against local policy: +│ - reject if deadline <= now (expired) +│ - reject if deadline > now + accusationVoteValidity + skew +│ - reject all peer accusations when accusationVoteValidity == 0 +│ - `skew` defaults to 30s and is configurable via +│ `ACCUSATION_DEADLINE_SKEW_SECS` on the node process │ -├─ 3. Compute accusation_id: +├─ 3. Verify accuser's ECDSA signature on accusation digest +│ +├─ 4. Compute accusation_id: │ keccak256(abi.encodePacked(chainId, e3Id, accused, proofType)) │ → Deterministic: all nodes compute same ID for same accusation │ -├─ 4. Determine own vote based on local verification cache: +├─ 5. Determine own vote based on local verification cache: │ │ │ ├─ Case A: We already FAILED verification for (accused, proof_type): │ │ → Vote agrees = true @@ -351,7 +358,7 @@ ProofFailureAccusation arrives via P2P from another committee member │ │ → Vote after re-verification completes │ └─ For other proofs: vote agrees = false (no local evidence) │ -├─ 5. Create and SIGN vote: +├─ 6. Create and SIGN vote: │ AccusationVote { │ e3_id, accusation_id, voter: my_address, │ agrees: , data_hash, @@ -359,7 +366,7 @@ ProofFailureAccusation arrives via P2P from another committee member │ } │ → Broadcast via P2P gossip │ -└─ 6. Check quorum immediately +└─ 7. Check quorum immediately ``` #### Step 3: Vote Digest & Accusation ID (Must Match Solidity) diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 7b0960a81..a3ae8c55d 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -44,7 +44,7 @@ use e3_zk_prover::{ use libp2p::PeerId; use std::time::Duration; use std::{collections::HashMap, path::PathBuf, sync::Arc}; -use tracing::{error, info}; +use tracing::{error, info, warn}; #[derive(Clone, Debug)] enum EventSystemType { @@ -685,19 +685,36 @@ impl CiphernodeBuilder { )) } + // Clock-skew allowance for peer accusation deadlines. + let accusation_deadline_skew_secs = match std::env::var("ACCUSATION_DEADLINE_SKEW_SECS") { + Ok(raw) => match raw.parse::() { + Ok(v) => v, + Err(err) => { + warn!( + value = %raw, + error = %err, + "invalid ACCUSATION_DEADLINE_SKEW_SECS; falling back to default" + ); + 30 + } + }, + Err(_) => 30, + }; + // AccusationManager extension — per-E3 fault attribution quorum { let signer = provider_cache.ensure_signer().await?; let slashing_manager_addr = self.resolve_slashing_manager()?; info!( chains = accusation_vote_validity_by_chain.len(), - "Setting up AccusationManagerExtension" + accusation_deadline_skew_secs, "Setting up AccusationManagerExtension" ); e3_builder = e3_builder.with(AccusationManagerExtension::create( &bus, signer, slashing_manager_addr, accusation_vote_validity_by_chain, + accusation_deadline_skew_secs, )); } diff --git a/crates/events/src/enclave_event/accusation_quorum_reached.rs b/crates/events/src/enclave_event/accusation_quorum_reached.rs index 5c2fc77f5..bd2e4570f 100644 --- a/crates/events/src/enclave_event/accusation_quorum_reached.rs +++ b/crates/events/src/enclave_event/accusation_quorum_reached.rs @@ -65,13 +65,11 @@ pub struct AccusationQuorumReached { /// Raw `abi.encode(proof.data, public_signals)` — preimage of every voter's /// `data_hash`. /// - /// **Off-chain audit metadata only.** The current Solidity - /// `SlashingManager.proposeSlash` no longer recomputes - /// `keccak256(evidence)` on chain (it equates voter `dataHash`es directly - /// for equivocation detection). The preimage is kept here so off-chain - /// observers can independently verify what every voter claimed to see - /// without re-fetching the proof from peers. Empty when this node didn't - /// have the raw bytes locally (e.g. consistency-violation path). + /// Consumed by Lane A slash submission: on-chain verifier checks + /// `keccak256(evidence) == sharedDataHash`. + /// Empty when this node didn't have raw bytes locally (e.g. + /// consistency-violation path), in which case submitter should skip + /// submission because on-chain binding cannot be proven. #[serde(default)] pub evidence: Bytes, } diff --git a/crates/evm/src/slashing_manager_sol_writer.rs b/crates/evm/src/slashing_manager_sol_writer.rs index 6bacf0867..1525e28aa 100644 --- a/crates/evm/src/slashing_manager_sol_writer.rs +++ b/crates/evm/src/slashing_manager_sol_writer.rs @@ -196,7 +196,7 @@ impl Handler /// Encode `AccusationQuorumReached` into the attestation evidence format expected /// by both `SlashingManager.proposeSlash()` and `SlashingManager.proposeSlashByDkgParty()`: -/// `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, uint256 deadline, bytes[] signatures)` +/// `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, bytes evidence, uint256 deadline, bytes[] signatures)` /// /// Voters are sorted ascending by address to satisfy the contract's duplicate-prevention /// check. All `votes_for` share the same `deadline` (the accuser stamps one value at @@ -205,7 +205,7 @@ impl Handler /// if `votes_for` is empty — the on-chain submitter must skip the submission in that /// case rather than send malformed calldata. pub fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Option> { - if data.votes_for.is_empty() { + if data.votes_for.is_empty() || data.evidence.is_empty() { return None; } @@ -216,6 +216,7 @@ pub fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Option = votes.iter().map(|v| v.voter).collect(); let data_hashes: Vec<[u8; 32]> = votes.iter().map(|v| v.data_hash).collect(); + let evidence = Bytes::from(data.evidence.clone()); // All voters signed the same deadline (enforced off-chain by AccusationManager); // pick any one — the first vote suffices. let deadline = U256::from(votes[0].deadline); @@ -224,7 +225,17 @@ pub fn encode_attestation_evidence(data: &AccusationQuorumReached) -> Option( @@ -248,11 +259,11 @@ async fn submit_slash_proposal( e3_id = %data.e3_id, accused = %operator, outcome = %data.outcome, - "Refusing to submit proposeSlash: AccusationQuorumReached carries no \ - agreeing votes — upstream quorum invariant violated, dropping submission" + "Refusing to submit proposeSlash: AccusationQuorumReached has empty \ + votes_for or empty evidence preimage — submission dropped" ); return Err(anyhow::anyhow!( - "AccusationQuorumReached has empty votes_for; refused proposeSlash submission \ + "AccusationQuorumReached has empty votes_for or evidence; refused proposeSlash submission \ (e3_id={}, accused={})", data.e3_id, operator diff --git a/crates/zk-prover/src/actors/accusation_manager.rs b/crates/zk-prover/src/actors/accusation_manager.rs index 40f61d040..e6dceb21b 100644 --- a/crates/zk-prover/src/actors/accusation_manager.rs +++ b/crates/zk-prover/src/actors/accusation_manager.rs @@ -49,6 +49,9 @@ use tracing::{error, info, warn}; /// How long to wait for votes before declaring the accusation inconclusive. const DEFAULT_VOTE_TIMEOUT: Duration = Duration::from_secs(300); // 5 minutes +/// Default clock-skew allowance when validating peer-stamped accusation deadlines. +#[cfg(test)] +const DEFAULT_ACCUSATION_DEADLINE_SKEW_SECS: u64 = 30; /// Abstraction over wall-clock time so the deadline-stamping logic is /// deterministically testable. Production uses [`SystemClock`], which reads @@ -182,6 +185,8 @@ pub struct AccusationManager { /// requires a node restart to take effect — same lifecycle as the fold /// attestation verifier. vote_validity_secs: u64, + /// Clock-skew allowance when validating peer accusation deadlines. + accusation_deadline_skew_secs: u64, /// Wall-clock source used to derive accusation deadlines. Production uses /// [`SystemClock`]; tests can inject a deterministic mock. @@ -203,6 +208,7 @@ impl AccusationManager { committee: Vec

, threshold_m: usize, vote_validity_secs: u64, + accusation_deadline_skew_secs: u64, params_preset: e3_fhe_params::BfvPreset, ) -> Self { Self::new_with_clock( @@ -213,6 +219,7 @@ impl AccusationManager { committee, threshold_m, vote_validity_secs, + accusation_deadline_skew_secs, params_preset, Arc::new(SystemClock), ) @@ -228,6 +235,7 @@ impl AccusationManager { committee: Vec
, threshold_m: usize, vote_validity_secs: u64, + accusation_deadline_skew_secs: u64, params_preset: e3_fhe_params::BfvPreset, clock: Arc, ) -> Self { @@ -247,6 +255,7 @@ impl AccusationManager { pending_reverifications: HashMap::new(), vote_timeout: DEFAULT_VOTE_TIMEOUT, vote_validity_secs, + accusation_deadline_skew_secs, clock, params_preset, } @@ -260,6 +269,7 @@ impl AccusationManager { committee: Vec
, threshold_m: usize, vote_validity_secs: u64, + accusation_deadline_skew_secs: u64, params_preset: e3_fhe_params::BfvPreset, ) -> Addr { let addr = Self::new( @@ -270,6 +280,7 @@ impl AccusationManager { committee, threshold_m, vote_validity_secs, + accusation_deadline_skew_secs, params_preset, ) .start(); @@ -307,6 +318,28 @@ impl AccusationManager { .saturating_add(self.vote_validity_secs) } + /// Validate a peer-provided accusation deadline against this node's local + /// vote-validity policy and wall clock. + /// + /// Accept iff: + /// - validity is enabled (`vote_validity_secs > 0`) + /// - deadline is strictly in the future + /// - deadline is not farther than `now + vote_validity_secs + skew` + fn is_peer_deadline_acceptable( + deadline: u64, + now: u64, + vote_validity_secs: u64, + skew_secs: u64, + ) -> bool { + if vote_validity_secs == 0 { + return false; + } + let max_deadline = now + .saturating_add(vote_validity_secs) + .saturating_add(skew_secs); + deadline > now && deadline <= max_deadline + } + // ─── Accusation ID computation ─────────────────────────────────────── /// Compute a deterministic ID for an accusation based on its key fields. @@ -646,6 +679,17 @@ impl AccusationManager { return; } + // Governance-disabled validity window means no accusation voting should + // be produced by this node. + if self.vote_validity_secs == 0 { + warn!( + "Refusing accusation initiation for {:?} on E3 {}: vote_validity_secs is 0", + accused_address, self.e3_id + ); + self.accused_proofs.remove(&key); + return; + } + // Pick the on-chain validity deadline once per accusation. Every voter // (including ourselves below) signs the same value; otherwise the // aggregated evidence cannot be encoded as a single `deadline`. @@ -747,6 +791,29 @@ impl AccusationManager { return; } + let now = self.clock.unix_now_secs(); + if !Self::is_peer_deadline_acceptable( + accusation.deadline, + now, + self.vote_validity_secs, + self.accusation_deadline_skew_secs, + ) { + let max_deadline = now + .saturating_add(self.vote_validity_secs) + .saturating_add(self.accusation_deadline_skew_secs); + warn!( + "Ignoring accusation from {} — deadline {} outside local validity window \ + (now={}, vote_validity_secs={}, skew_secs={}, max_accepted_deadline={})", + accusation.accuser, + accusation.deadline, + now, + self.vote_validity_secs, + self.accusation_deadline_skew_secs, + max_deadline + ); + return; + } + // Verify accuser is in committee if !self.committee.contains(&accusation.accuser) { warn!( @@ -1710,4 +1777,33 @@ mod tests { let b = AccusationManager::accusation_digest(&make(1_700_000_001)); assert_ne!(a, b, "deadline must be part of the accusation digest"); } + + #[test] + fn peer_deadline_acceptance_enforces_local_window() { + let now = 1_700_000_000u64; + let validity = 1_800u64; + let skew = DEFAULT_ACCUSATION_DEADLINE_SKEW_SECS; + let max_ok = now + validity + skew; + + assert!( + !AccusationManager::is_peer_deadline_acceptable(now, now, validity, skew), + "deadline equal to now must be rejected" + ); + assert!( + !AccusationManager::is_peer_deadline_acceptable(now - 1, now, validity, skew), + "expired deadline must be rejected" + ); + assert!( + AccusationManager::is_peer_deadline_acceptable(max_ok, now, validity, skew), + "deadline at upper bound must be accepted" + ); + assert!( + !AccusationManager::is_peer_deadline_acceptable(max_ok + 1, now, validity, skew), + "far-future deadline must be rejected" + ); + assert!( + !AccusationManager::is_peer_deadline_acceptable(now + 10, now, 0, skew), + "vote_validity_secs=0 must reject peer accusations" + ); + } } diff --git a/crates/zk-prover/src/actors/accusation_manager_ext.rs b/crates/zk-prover/src/actors/accusation_manager_ext.rs index 127eef442..2e5c74568 100644 --- a/crates/zk-prover/src/actors/accusation_manager_ext.rs +++ b/crates/zk-prover/src/actors/accusation_manager_ext.rs @@ -32,6 +32,8 @@ pub struct AccusationManagerExtension { /// governance changes require a node restart to take effect (same lifecycle /// contract as `slashing_manager`). vote_validity_secs_by_chain: HashMap, + /// Clock-skew allowance for peer accusation deadlines. + accusation_deadline_skew_secs: u64, } impl AccusationManagerExtension { @@ -40,12 +42,14 @@ impl AccusationManagerExtension { signer: PrivateKeySigner, slashing_manager: Address, vote_validity_secs_by_chain: HashMap, + accusation_deadline_skew_secs: u64, ) -> Box { Box::new(Self { bus: bus.clone(), signer: signer.clone(), slashing_manager, vote_validity_secs_by_chain, + accusation_deadline_skew_secs, }) } @@ -123,6 +127,7 @@ impl E3Extension for AccusationManagerExtension { committee_addresses, threshold_m, vote_validity_secs, + self.accusation_deadline_skew_secs, meta.params_preset, ); diff --git a/docs/pages/internals/dkg.mdx b/docs/pages/internals/dkg.mdx index 49e86a197..28b217ac1 100644 --- a/docs/pages/internals/dkg.mdx +++ b/docs/pages/internals/dkg.mdx @@ -522,7 +522,7 @@ differs the proof is rejected — and the plaintext is decoded from the proof's When fault attribution leads to a slash, the off-chain accusation quorum produces a payload that [`SlashingManager.proposeSlash`](https://github.com/gnosisguild/enclave/blob/main/packages/enclave-contracts/contracts/slashing/SlashingManager.sol) decodes as -`(uint256 proofType, address[] voters, bytes32[] dataHashes, uint256 deadline, bytes[] signatures)`. +`(uint256 proofType, address[] voters, bytes32[] dataHashes, bytes evidence, uint256 deadline, bytes[] signatures)`. `_verifyAttestationEvidence` enforces: - ≥ `M` voters, sorted ascending, accused excluded, each producing a canonical EIP-712 typed-data @@ -534,6 +534,7 @@ decodes as `VOTE_DOMAIN_VERSION` / `VOTE_TYPEHASH_STR`. - All `dataHashes[i]` equal a common `dataHash` (voters must agree on the same evidence, not just "something bad"). +- `keccak256(evidence) == dataHash` (explicit preimage binding on chain). - `block.timestamp <= deadline`. Every voter signs the same `deadline` value — the accuser stamps it at proposal time as `now + accusationVoteValidity` (a registry-wide governance knob, see the subsection below), and `AccusationManager::on_vote_received` rejects peers' votes whose deadline diff --git a/examples/CRISP/server/src/deployments.rs b/examples/CRISP/server/src/deployments.rs index 3d4fad5f5..4f60f5c6a 100644 --- a/examples/CRISP/server/src/deployments.rs +++ b/examples/CRISP/server/src/deployments.rs @@ -43,10 +43,9 @@ pub fn localhost_mock_voting_token() -> Result> { if !path.exists() { return Ok(None); } - let raw = std::fs::read_to_string(&path) - .with_context(|| format!("read {}", path.display()))?; - let file: DeployedContractsFile = serde_json::from_str(&raw) - .with_context(|| format!("parse {}", path.display()))?; + let raw = std::fs::read_to_string(&path).with_context(|| format!("read {}", path.display()))?; + let file: DeployedContractsFile = + serde_json::from_str(&raw).with_context(|| format!("parse {}", path.display()))?; Ok(file .localhost .and_then(|c| c.mock_voting_token) @@ -59,10 +58,9 @@ pub fn localhost_crisp_program() -> Result> { if !path.exists() { return Ok(None); } - let raw = std::fs::read_to_string(&path) - .with_context(|| format!("read {}", path.display()))?; - let file: DeployedContractsFile = serde_json::from_str(&raw) - .with_context(|| format!("parse {}", path.display()))?; + let raw = std::fs::read_to_string(&path).with_context(|| format!("read {}", path.display()))?; + let file: DeployedContractsFile = + serde_json::from_str(&raw).with_context(|| format!("parse {}", path.display()))?; Ok(file .localhost .and_then(|c| c.crisp_program) diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index 448c3ab8f..843bc7966 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -3159,5 +3159,5 @@ }, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" + "buildInfoId": "solc-0_8_28-834ec0837fe1c9299862cfab43bdd2eef32c6092" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index d839196ce..a22990606 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -1233,5 +1233,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" + "buildInfoId": "solc-0_8_28-834ec0837fe1c9299862cfab43bdd2eef32c6092" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index 183629e79..f58fbbe9a 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -1981,5 +1981,5 @@ }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-a6902ac15e994e1c055eb02433db296d1823fdfc" + "buildInfoId": "solc-0_8_28-834ec0837fe1c9299862cfab43bdd2eef32c6092" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol b/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol index d5ff6667c..866ca5dfb 100644 --- a/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol +++ b/packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol @@ -495,19 +495,21 @@ interface ISlashingManager { * cross-reason replay attacks. * Evidence format: * abi.encode(uint256 proofType, - * address[] voters, bool[] agrees, bytes32[] dataHashes, bytes[] signatures) + * address[] voters, bytes32[] dataHashes, bytes evidence, uint256 deadline, bytes[] signatures) * Each voter must have signed: personal_sign(keccak256(abi.encode(VOTE_TYPEHASH, - * block.chainid, e3Id, accusationId, voter, agrees, dataHash))) + * e3Id, accusationId, voter, dataHash, deadline))) * where accusationId = keccak256(abi.encodePacked(block.chainid, e3Id, operator, proofType)) * Verifications performed: * 1. Number of votes >= committee threshold M * 2. Voters are sorted ascending (prevents duplicates) * 3. Each voter is a committee member for this E3 * 4. Each vote signature recovers to the declared voter - * 5. All votes agree the proof is invalid (agrees == true) + * 5. All votes carry the same `dataHash` (no equivocation) + * 6. `keccak256(evidence) == dataHash` * @param e3Id ID of the E3 computation this slash relates to * @param operator Address of the ciphernode operator to slash (must be non-zero) - * @param proof Attestation evidence: abi.encode(proofType, voters, agrees, dataHashes, signatures) + * @param proof Attestation evidence: + * abi.encode(proofType, voters, dataHashes, evidence, deadline, signatures) * @return proposalId Sequential ID of the created proposal */ function proposeSlash( diff --git a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol index cb502199e..0c4b3a041 100644 --- a/packages/enclave-contracts/contracts/slashing/SlashingManager.sol +++ b/packages/enclave-contracts/contracts/slashing/SlashingManager.sol @@ -344,9 +344,9 @@ contract SlashingManager is /// Execution is atomic when `policy.appealWindow == 0`, otherwise deferred so /// the accused can {fileAppeal}. Evidence format: /// `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, - /// uint256 deadline, bytes[] signatures)`. Voters sign the EIP-712 + /// bytes evidence, uint256 deadline, bytes[] signatures)`. Voters sign the EIP-712 /// `AccusationVote` against this contract's domain; all `dataHash` values - /// must be identical or the call reverts with `EquivocationDetected`. + /// must be identical and equal `keccak256(evidence)`. function proposeSlash( uint256 e3Id, address operator, @@ -548,7 +548,8 @@ contract SlashingManager is /// @dev Verifies Lane A attestation evidence: decodes, checks quorum (>= M), verifies /// each EIP-712 `AccusationVote` signature, confirms voters are active committee /// members, enforces the shared `deadline`, and rejects equivocation (all - /// `dataHash` values must match). Voters must be sorted ascending (no duplicates). + /// `dataHash` values must match and bind to `keccak256(evidence)`). + /// Voters must be sorted ascending (no duplicates). function _verifyAttestationEvidence( bytes calldata proof, uint256 e3Id, @@ -558,11 +559,12 @@ contract SlashingManager is uint256 proofType, address[] memory voters, bytes32[] memory dataHashes, + bytes memory evidence, uint256 deadline, bytes[] memory signatures ) = abi.decode( proof, - (uint256, address[], bytes32[], uint256, bytes[]) + (uint256, address[], bytes32[], bytes, uint256, bytes[]) ); uint256 numVotes = voters.length; @@ -590,6 +592,7 @@ contract SlashingManager is // one voter is signing inconsistent statements and the attestation must not be // accepted as a single fault witness. bytes32 sharedDataHash = dataHashes[0]; + require(keccak256(evidence) == sharedDataHash, InvalidProof()); // Verify each vote signature and membership address prevVoter = address(0); diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index 692239315..baf03c8bf 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -352,7 +352,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(evidence1), + evidence1, ); await slashingManager.proposeSlash( 0, @@ -423,7 +423,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(ev1), + ev1, ); await slashingManager.proposeSlash( 0, @@ -443,7 +443,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 7, // C6ThresholdShareDecryption — different proofType 31337, - ethers.keccak256(ev2), + ev2, ); await slashingManager.proposeSlash( 0, @@ -560,7 +560,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(evExpel1), + evExpel1, ); await slashingManager.proposeSlash( 0, @@ -577,7 +577,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(evExpel2), + evExpel2, ); await slashingManager.proposeSlash( 0, @@ -710,7 +710,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(evExpelOp1), + evExpelOp1, ); await slashingManager.proposeSlash( 0, @@ -727,7 +727,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(evExpelOp2), + evExpelOp2, ); await slashingManager.proposeSlash( 0, @@ -755,7 +755,7 @@ describe("Committee Expulsion & Fault Tolerance", function () { await slashingManager.getAddress(), 0, 31337, - ethers.keccak256(ethers.toUtf8Bytes("expel-op3")), + ethers.hexlify(ethers.toUtf8Bytes("expel-op3")), ), ), ).to.be.revertedWithCustomError( diff --git a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts index 03dd7ca9e..eb4020eef 100644 --- a/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts +++ b/packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts @@ -580,6 +580,8 @@ describe("SlashingManager", function () { ), ); const deadline = ethers.MaxUint256; + const evidence = ethers.hexlify(ethers.toUtf8Bytes("invalid-signature")); + const evidenceHash = ethers.keccak256(evidence); // Sort voters ascending const sortedVoters = [voter1Addr, voter2Addr].sort((a, b) => @@ -609,12 +611,12 @@ describe("SlashingManager", function () { const dataHashes: string[] = []; const signatures: string[] = []; - // `dataHash` must equal `keccak256(evidence)` per the on-chain check. - // This case signs `ZeroHash` deliberately (mismatched vs real evidence). + // Keep `dataHash == keccak256(evidence)` so this test isolates + // signature forgery and reverts with InvalidVoteSignature. for (let i = 0; i < sortedVoters.length; i++) { const voterAddr = sortedVoters[i]; voters.push(voterAddr); - dataHashes.push(ethers.ZeroHash); + dataHashes.push(evidenceHash); // For the second voter, use notTheOwner to sign (wrong signer) const signerToUse = @@ -623,7 +625,7 @@ describe("SlashingManager", function () { e3Id: 0, accusationId, voter: voterAddr, - dataHash: ethers.ZeroHash, + dataHash: evidenceHash, deadline, }; const signature = await signerToUse.signTypedData(domain, types, value); @@ -631,8 +633,8 @@ describe("SlashingManager", function () { } const proof = abiCoder.encode( - ["uint256", "address[]", "bytes32[]", "uint256", "bytes[]"], - [0, voters, dataHashes, deadline, signatures], + ["uint256", "address[]", "bytes32[]", "bytes", "uint256", "bytes[]"], + [0, voters, dataHashes, evidence, deadline, signatures], ); await expect( @@ -676,6 +678,51 @@ describe("SlashingManager", function () { ).to.be.revertedWithCustomError(slashingManager, "VoterNotInCommittee"); }); + it("should revert if evidence preimage does not match signed dataHash", async function () { + const { + slashingManager, + proposer, + operatorAddress, + voter1, + voter2, + mockCiphernodeRegistry, + } = await loadFixture(setup); + + const proofPolicy = buildProofPolicy(); + await slashingManager.setSlashPolicy(REASON_PT_0, proofPolicy); + + const e3Id = 0; + const voter1Addr = await voter1.getAddress(); + const voter2Addr = await voter2.getAddress(); + await mockCiphernodeRegistry.setCommitteeNodes(e3Id, [ + operatorAddress, + voter1Addr, + voter2Addr, + ]); + await mockCiphernodeRegistry.setThreshold(e3Id, 2); + + const evidence = ethers.hexlify( + ethers.toUtf8Bytes("mismatched-preimage"), + ); + const proof = await signAndEncodeAttestation( + [voter1, voter2], + e3Id, + operatorAddress, + await slashingManager.getAddress(), + 0, + 31337, + evidence, + ethers.MaxUint256, + ethers.ZeroHash, // deliberately mismatched vs keccak256(evidence) + ); + + await expect( + slashingManager + .connect(proposer) + .proposeSlash(e3Id, operatorAddress, proof), + ).to.be.revertedWithCustomError(slashingManager, "InvalidProof"); + }); + it("should revert if attestation deadline has expired", async function () { const { slashingManager, diff --git a/packages/enclave-contracts/test/fixtures/attestation.ts b/packages/enclave-contracts/test/fixtures/attestation.ts index 44ef21a8b..62efafbd9 100644 --- a/packages/enclave-contracts/test/fixtures/attestation.ts +++ b/packages/enclave-contracts/test/fixtures/attestation.ts @@ -29,8 +29,8 @@ const NO_EXPIRY = ethers.MaxUint256; * Helper to create signed committee attestation evidence for Lane A. * * Returns `abi.encode(uint256 proofType, address[] voters, bytes32[] dataHashes, - * uint256 deadline, bytes[] signatures)` with voters sorted - * ascending by address. + * bytes evidence, uint256 deadline, bytes[] signatures)` with + * voters sorted ascending by address. * * Each voter signs the EIP-712 `AccusationVote` struct against the * `EnclaveSlashing/1` domain anchored at `verifyingContract`. This binds the @@ -43,9 +43,10 @@ const NO_EXPIRY = ethers.MaxUint256; * @param verifyingContract - Address of the SlashingManager (EIP-712 domain). * @param proofType - Numeric proof type, mapped to a slash reason on-chain. * @param chainId - Chain ID for the EIP-712 domain. Defaults to 31337 (hardhat). - * @param dataHash - Witness hash. All voters must sign the same `dataHash` - * or `proposeSlash` reverts with `EquivocationDetected`. + * @param evidence - Evidence preimage bytes. All voters sign + * `keccak256(evidence)` as `dataHash`. * @param deadline - Optional unix expiry. Defaults to MaxUint256. + * @param dataHashOverride - Optional negative-test override for signed `dataHash`. */ export async function signAndEncodeAttestation( voterSigners: Signer[], @@ -54,8 +55,9 @@ export async function signAndEncodeAttestation( verifyingContract: string, proofType: number = 0, chainId: number = 31337, - dataHash: string = ethers.ZeroHash, + evidence: string = ethers.hexlify(ethers.toUtf8Bytes("lane-a-attestation")), deadline: bigint = NO_EXPIRY, + dataHashOverride?: string, ): Promise { const accusationId = ethers.keccak256( ethers.solidityPacked( @@ -98,6 +100,7 @@ export async function signAndEncodeAttestation( const voters: string[] = []; const dataHashes: string[] = []; const signatures: string[] = []; + const dataHash = dataHashOverride ?? ethers.keccak256(evidence); for (const { signer, address: voterAddress } of signersWithAddrs) { voters.push(voterAddress); @@ -128,7 +131,7 @@ export async function signAndEncodeAttestation( void abiCoder; return ethers.AbiCoder.defaultAbiCoder().encode( - ["uint256", "address[]", "bytes32[]", "uint256", "bytes[]"], - [proofType, voters, dataHashes, deadline, signatures], + ["uint256", "address[]", "bytes32[]", "bytes", "uint256", "bytes[]"], + [proofType, voters, dataHashes, evidence, deadline, signatures], ); }